useyuva 0.0.8 → 0.0.10

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 (53) hide show
  1. package/README.md +98 -6
  2. package/dist/esm/analytics.js +1 -0
  3. package/dist/esm/chat.js +2 -2
  4. package/dist/esm/client-9wy8zy77.js +1 -0
  5. package/dist/esm/docs.js +2 -2
  6. package/dist/esm/index-98r96a7e.js +2 -0
  7. package/dist/esm/index-c6q8br7d.js +2 -0
  8. package/dist/esm/index-geeh5h34.js +12 -0
  9. package/dist/esm/index-gk84nm8f.js +2 -0
  10. package/dist/esm/index.js +1 -1
  11. package/dist/esm/nuxt/module.js +7 -3
  12. package/dist/esm/nuxt/runtime/analytics.js +6 -0
  13. package/dist/esm/nuxt/runtime/components.js +32 -0
  14. package/dist/esm/nuxt/runtime/plugin.js +11 -0
  15. package/dist/esm/react.js +2 -1
  16. package/dist/esm/subscribe.js +33 -0
  17. package/dist/esm/{thread-3ydc3xfc.js → thread-btm71nxt.js} +2 -2
  18. package/dist/esm/yuva-chat-nkt3xjhb.js +193 -0
  19. package/dist/types/analytics/core.d.ts +47 -0
  20. package/dist/types/analytics/handle.d.ts +3 -0
  21. package/dist/types/analytics.d.ts +1 -0
  22. package/dist/types/client/api.d.ts +11 -0
  23. package/dist/types/client/client.d.ts +6 -1
  24. package/dist/types/i18n.d.ts +2 -2
  25. package/dist/types/index.d.ts +1 -1
  26. package/dist/types/locales/de/docs.d.ts +2 -0
  27. package/dist/types/locales/de/loader.d.ts +2 -0
  28. package/dist/types/locales/de/panel.d.ts +2 -0
  29. package/dist/types/locales/de/subscribe.d.ts +2 -0
  30. package/dist/types/locales/de/thread.d.ts +2 -0
  31. package/dist/types/locales/en/subscribe.d.ts +2 -0
  32. package/dist/types/locales/tr/subscribe.d.ts +2 -0
  33. package/dist/types/nuxt/module.d.ts +8 -1
  34. package/dist/types/nuxt/runtime/analytics.d.ts +2 -0
  35. package/dist/types/nuxt/runtime/components.d.ts +34 -0
  36. package/dist/types/nuxt/runtime/plugin.d.ts +6 -0
  37. package/dist/types/react.d.ts +18 -0
  38. package/dist/types/schema.gen.d.ts +15384 -5025
  39. package/dist/types/subscribe/element.d.ts +34 -0
  40. package/dist/types/subscribe.d.ts +4 -0
  41. package/dist/types/translate.d.ts +3 -0
  42. package/dist/widget/yuva-docs-thread-8st0egsq.js +61 -0
  43. package/dist/widget/yuva-subscribe-client-jya732vb.js +11 -0
  44. package/dist/yuva-analytics.js +1 -0
  45. package/dist/yuva-chat.js +6 -6
  46. package/dist/yuva-docs.js +6 -6
  47. package/dist/yuva-subscribe.js +33 -0
  48. package/dist/yuva.js +2 -2
  49. package/package.json +27 -4
  50. package/dist/esm/index-dj5dhqc1.js +0 -12
  51. package/dist/esm/index-e2tek7yk.js +0 -2
  52. package/dist/esm/yuva-chat-86e4mz63.js +0 -193
  53. package/dist/widget/yuva-docs-thread-4580g2ny.js +0 -61
package/README.md CHANGED
@@ -7,8 +7,10 @@ JavaScript SDK for [Yuva](https://github.com/productdevbook/yuva), open-source c
7
7
  | `useyuva` | `createYuvaClient()`: the contact side (`/client/v1`) without DOM. Browsers, Node 22+, Bun, Deno, workers. |
8
8
  | `useyuva/chat` | The `<yuva-chat>` web component, built on that client. Browser only. |
9
9
  | `useyuva/docs` | `<yuva-page-feedback>` and `<yuva-discussions>` for documentation pages. Registers in the browser only, so it is safe to import during SSR. |
10
- | `useyuva/react` | `YuvaProvider`, `useConversations`, `useMessages`. React 18+ is an optional peer. |
11
- | `useyuva/nuxt` | A Nuxt module: `YuvaChat`, `YuvaPageFeedback` and `YuvaDiscussions` components, configured once in `nuxt.config`. |
10
+ | `useyuva/subscribe` | `<yuva-subscribe>`, a mailing list sign-up form. Registers in the browser only. |
11
+ | `useyuva/analytics` | `createAnalytics()`: cookieless pageviews and engagement for an analytics property. Does nothing during SSR. |
12
+ | `useyuva/react` | `YuvaProvider`, `useConversations`, `useMessages`, `YuvaAnalytics`, `useYuvaAnalytics`. React 18+ is an optional peer. |
13
+ | `useyuva/nuxt` | A Nuxt module: `YuvaChat`, `YuvaPageFeedback`, `YuvaDiscussions` and `YuvaSubscribe` components, configured once in `nuxt.config`. |
12
14
  | `useyuva/api` | A typed `/v1` client for backends (API key), generated from the OpenAPI contract. |
13
15
 
14
16
  ```sh
@@ -16,7 +18,9 @@ npm install useyuva
16
18
  ```
17
19
 
18
20
  Without a bundler, the server hosts the widget itself: `<script src="https://<your-yuva>/yuva.js" defer></script>`,
19
- and the documentation page elements without the chat: `<script src="https://<your-yuva>/yuva-docs.js" defer></script>`.
21
+ the documentation page elements without the chat: `<script src="https://<your-yuva>/yuva-docs.js" defer></script>`,
22
+ the subscribe form: `<script src="https://<your-yuva>/yuva-subscribe.js" defer></script>`,
23
+ and analytics: `<script src="https://<your-yuva>/yuva-analytics.js" data-key="yuva_ak_…" defer></script>`.
20
24
 
21
25
  ## Client
22
26
 
@@ -108,6 +112,11 @@ Reader sign-in:
108
112
  - `discussionWriter()` resolves to `"reader"`, `"contact"` (an identity token) or `null` (read only).
109
113
  - `openReaderPopup(server, channel)` and `readerConnectUrl(server, channel, origin)` are the same popup without a client.
110
114
 
115
+ Mailing lists:
116
+
117
+ - `lists()` returns the channel's public lists and a `form_token`; `subscribe({ email, list_ids, page_url, consent_text, form_token })` answers `{ confirmation }` the same way for every address. Send the token back 2 seconds to 2 hours after `lists()`.
118
+ - With an identity token: `subscriptions()` lists the public lists with the contact's `state`; `subscribeList(listId, { consent_text })` and `unsubscribeList(listId)` change it.
119
+
111
120
  Errors worth handling: `403 sign_in_required`, `discussions_off`, `discussion_locked`, `edit_window_closed`, `forbidden`; `400 page_origin`; `429 rate_limited`.
112
121
 
113
122
  ## Chat element
@@ -148,18 +157,101 @@ Events: `yuva-rating` (`{ page, rating, previous }`), `yuva-feedback` (`{ page,
148
157
  and `yuva-reader` (`{ name, initials }` or `null`). In React use `YuvaPageFeedback` and
149
158
  `YuvaDiscussions` from `useyuva/react` (`onDiscussion`, `onPost`, `onReader`).
150
159
 
160
+ ## Subscribe form
161
+
162
+ ```ts
163
+ import "useyuva/subscribe";
164
+ ```
165
+
166
+ ```html
167
+ <yuva-subscribe channel="yuva_pk_…" server="https://support.example.com" layout="inline"></yuva-subscribe>
168
+ ```
169
+
170
+ Takes `channel`, `server`, `locale`, `dir` and `identity-token`, plus `lists` (comma-separated ids;
171
+ empty for every public list, with a checkbox each when there is more than one), `layout` (`stacked`
172
+ or `inline`), `name` (`optional` or `required` adds a name field), `button-text`, `consent` (your
173
+ own consent sentence) and `privacy-url`. The consent sentence next to the unticked box is sent as it
174
+ is shown. With an identity token it shows a switch per list instead of the e-mail field. Style it
175
+ with `--yuva-accent`, `--yuva-on-accent`, `--yuva-radius`, `--yuva-font` and
176
+ `::part(field|button|consent|status)`. Events: `yuva-subscribe` (`{ lists, confirmation }`) and
177
+ `yuva-subscribe-error` (`{ code }`); `YuvaSubscribe` in `useyuva/react` (`onSubscribe`, `onError`)
178
+ and Nuxt (`@subscribe`, `@error`).
179
+
180
+ ## Analytics
181
+
182
+ ```html
183
+ <script defer data-key="yuva_ak_…" src="https://support.example.com/yuva-analytics.js"></script>
184
+ ```
185
+
186
+ ```ts
187
+ import { createAnalytics } from "useyuva/analytics";
188
+
189
+ const analytics = createAnalytics({ server: "https://support.example.com", key: "yuva_ak_…" });
190
+ ```
191
+
192
+ Counts a pageview on load and on every History API navigation (`pushState`, back and forward; not
193
+ `replaceState`, not a query change), and one `engagement` per view with the visible and focused
194
+ time and the deepest scroll when the page is hidden or left. Events go out as `text/plain`, so the
195
+ browser sends no preflight; events of one moment share a request, and the last one is sent with
196
+ `sendBeacon`. A `403` from the server stops sending for the page.
197
+
198
+ Nothing is put on the device and no cookie is read: the only storage access is reading
199
+ `localStorage.yuva_optout`, written by `optOut()`. With `navigator.globalPrivacyControl`, Do Not
200
+ Track or `navigator.webdriver` nothing is sent at all.
201
+
202
+ | Attribute | Option | |
203
+ |---|---|---|
204
+ | `data-key` | `key` | The property's public key. |
205
+ | `data-api` | `server` | The Yuva server; the script defaults to the origin it was loaded from. Set it when you serve the endpoint from your own domain. |
206
+ | `data-hash` | `hash: true` | Hash routing: the fragment is part of the page. |
207
+ | `data-consent="required"` | `consent: "required"` | Send nothing until `consent({ analytics: true })`. |
208
+ | `data-manual` | `manual: true` | No automatic pageviews; call `pageview()`. |
209
+ | `data-outbound` | `outbound: true` | A click on a link to another host sends `outbound_click` with prop `url`. |
210
+ | `data-files` | `files: true` | A click on a link to a pdf, zip, dmg, exe, apk, csv, xls(x), doc(x), ppt(x), mp3 or mp4 file sends `file_download` with prop `url`. |
211
+ | `data-chat` | `chat: true` | Opening a `<yuva-chat>` on the page sends `chat_open`. |
212
+
213
+ Methods (`window.yuva` for the script): `pageview({ url?, referrer? })`,
214
+ `track(name, { props?, revenue? })`, `notFound()`, `consent({ analytics })` (or `consent(false)`),
215
+ `optOut()`, `optIn()`, and `destroy()`. Calls made before the script loads go to
216
+ `(window.yuvaq = window.yuvaq || []).push(["track", "Signup", { props: { plan: "pro" } }])`.
217
+
218
+ ```ts
219
+ analytics.track("Signup", { props: { plan: "pro", seats: 3 }, revenue: { amount: "29.90", currency: "EUR" } });
220
+ analytics.notFound(); // on the site's 404 page
221
+ ```
222
+
223
+ `track` sends a custom event on the current page: up to 30 props (keys of 1 to 300 characters, not
224
+ starting with `$` or `yuva_`; string, number or boolean values) and revenue as a decimal amount in an
225
+ ISO 4217 currency, which Yuva never converts. The server refuses `pageview`, `engagement`, `screen`
226
+ and names starting with `$` or `yuva_`. `notFound()` sends `not_found`, which does not stop the visit
227
+ from bouncing. Every event follows consent mode, the opt-out, GPC, Do Not Track and `webdriver`
228
+ like pageviews do. `<yuva-chat>` dispatches `yuva-open` (bubbling, composed) when its launcher
229
+ opens it; the embedded layout does not.
230
+ Links to `#yuva-optout` and `#yuva-optin` opt the visitor out and back in.
231
+
232
+ In React, render `<YuvaAnalytics server="…" publicKey="yuva_ak_…" outbound />` once (it renders nothing; the
233
+ other options are props too) and call
234
+ `useYuvaAnalytics()` anywhere for `pageview`, `track`, `notFound`, `consent`, `optOut` and `optIn`; calls before it has
235
+ loaded wait for it. The prop is `publicKey` because React keeps `key` for itself. In Nuxt, set `yuva.analytics` (below) and use `useYuvaAnalytics()`.
236
+
151
237
  ## Nuxt
152
238
 
153
239
  ```ts
154
240
  // nuxt.config.ts
155
241
  export default defineNuxtConfig({
156
242
  modules: ["useyuva/nuxt"],
157
- yuva: { server: "https://support.example.com", channel: "yuva_pk_…" },
243
+ yuva: {
244
+ server: "https://support.example.com",
245
+ channel: "yuva_pk_…",
246
+ analytics: { key: "yuva_ak_…" },
247
+ },
158
248
  });
159
249
  ```
160
250
 
161
- `NUXT_PUBLIC_YUVA_SERVER`, `NUXT_PUBLIC_YUVA_CHANNEL` and `NUXT_PUBLIC_YUVA_LOCALE` override the
162
- options at runtime. `<YuvaChat />`, `<YuvaPageFeedback />` and `<YuvaDiscussions />` are
251
+ `NUXT_PUBLIC_YUVA_SERVER`, `NUXT_PUBLIC_YUVA_CHANNEL`, `NUXT_PUBLIC_YUVA_LOCALE` and, when
252
+ `analytics` is set, `NUXT_PUBLIC_YUVA_ANALYTICS_KEY` override the options at runtime. `analytics`
253
+ takes `key`, `server` (defaults to `yuva.server`), `hash`, `consent`, `manual`, `outbound`, `files` and `chat`, adds a client
254
+ plugin and auto-imports `useYuvaAnalytics()`. `<YuvaChat />`, `<YuvaPageFeedback />`, `<YuvaDiscussions />` and `<YuvaSubscribe />` are
163
255
  auto-imported, render on the server and load their element in the browser when they mount. Props
164
256
  are the attributes in camelCase (`pageTitle`, `identityToken`, which also takes a function);
165
257
  events are `@rating`, `@feedback`, `@discussion`, `@post`, `@reader` and `@unread`. `yuva-*` tags are custom elements in
@@ -0,0 +1 @@
1
+ var OPT_OUT_KEY="yuva_optout",noop=()=>{};function createAnalytics(options){if(typeof window>"u"||!options.key)return{pageview:noop,track:noop,notFound:noop,consent:noop,optOut:noop,optIn:noop,destroy:noop};let nav=navigator,doc=document,storage=()=>{try{return localStorage}catch{}},url=`${options.server.replace(/\/$/,"")}/client/v1/analytics/${options.key}`,hash=options.hash,blocked=nav.globalPrivacyControl||nav.doNotTrack=="1"||nav.webdriver,off=blocked||storage()?.getItem("yuva_optout")=="1",consented=!options.consent,queue=[],view="",page="",referrer=doc.referrer,ms=0,since=0,depth=0,flush=(beacon)=>{let body=JSON.stringify(queue[1]?{e:queue}:queue[0]);if(queue[0]&&(queue=[],!(beacon&&nav.sendBeacon?.(url,body))))fetch(url,{method:"POST",body,keepalive:!0,credentials:"omit"}).then((res)=>void(res.status==403&&(off=blocked=!0)),noop)},send=(event)=>{if(off||!consented)return;if(hash)event.h=!0;if(options.consent)event.c={a:1};if(queue.push(event)<2)setTimeout(flush)},tick=()=>{let now=Date.now();if(since)ms+=now-since;since=doc.visibilityState=="visible"&&doc.hasFocus()?now:0},scroll=()=>{depth=Math.max(depth,Math.min(100,Math.round((scrollY+innerHeight)/doc.documentElement.scrollHeight*100))||0)},engagement=()=>{if(tick(),page&&ms>0)send({n:"engagement",u:page,ms:Math.min(ms,1800000),sd:depth});ms=0,depth=0,scroll()},key=()=>location.pathname+(hash?location.hash:""),pageview=(opts={})=>{if(off||!consented)return;engagement(),view=key(),page=opts.url||location.href,send({n:"pageview",u:page,r:opts.referrer??referrer,w:innerWidth}),referrer=void 0},track=(name,{props,revenue}={})=>send({n:name,u:page||location.href,p:props,$:revenue&&{a:revenue.amount,c:revenue.currency}}),auto=()=>!options.manual&&view!=key()&&pageview(),leave=()=>{engagement(),flush(!0)},optOut=()=>{off=!0,queue=[],storage()?.setItem("yuva_optout","1")},optIn=()=>{off=blocked,storage()?.removeItem("yuva_optout")},push=history.pushState,wrapped=function(...args){push.apply(this,args),auto()};history.pushState=wrapped;let on={popstate:auto,hashchange:auto,prerenderingchange:auto,scroll,focus:tick,blur:tick,pagehide:leave,visibilitychange:()=>doc.visibilityState=="hidden"?leave():tick(),click:(event)=>{let link=event.target.closest?.("a");if(!link)return;let href=link.getAttribute("href"),choice=href=="#yuva-optout"?optOut:href=="#yuva-optin"?optIn:0,name=options.files&&/\.(pdf|zip|dmg|exe|apk|csv|xlsx?|docx?|pptx?|mp[34])$/i.test(link.pathname)?"file_download":options.outbound&&link.host!=location.host&&"outbound_click";if(choice)event.preventDefault(),choice();else if(name&&/^https?:/.test(link.href))track(name,{props:{url:link.href}})}};if(options.chat)on["yuva-open"]=()=>track("chat_open");for(let t in on)addEventListener(t,on[t],!0);if(!doc.prerendering)auto();return{pageview,track,notFound:()=>track("not_found"),consent:(choice)=>{if(consented=choice===!0||!!choice.analytics,consented)auto();else queue=[]},optOut,optIn,destroy:()=>{if(leave(),off=!0,history.pushState==wrapped)history.pushState=push;for(let t in on)removeEventListener(t,on[t],!0)}}}export{OPT_OUT_KEY,createAnalytics};
package/dist/esm/chat.js CHANGED
@@ -1,4 +1,4 @@
1
- import{resolveLocale,directionOf,catalogFor,translatePlain,launcherOf,contrastOn,readStored,writeStored}from"./index-e2tek7yk.js";var styles=`
1
+ import{resolveLocale,catalogFor,translatePlain}from"./index-c6q8br7d.js";import{directionOf,launcherOf,contrastOn,readStored,writeStored}from"./index-98r96a7e.js";var styles=`
2
2
  :host {
3
3
  --yuva-accent: #2563eb;
4
4
  --yuva-on-accent: #ffffff;
@@ -106,4 +106,4 @@ import{resolveLocale,directionOf,catalogFor,translatePlain,launcherOf,contrastOn
106
106
  block-size: 100%;
107
107
  min-block-size: 320px;
108
108
  }
109
- `,chatIcon='<svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round" aria-hidden="true"><path d="M4 5h16v11H9l-5 4z"/></svg>',closeIcon='<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"/></svg>',scriptSrc=document.currentScript instanceof HTMLScriptElement?document.currentScript.src:"";class YuvaChatElement extends HTMLElement{static observedAttributes=["channel","server","layout","locale","dir","identity-token","open"];static loadPanel=()=>import(new URL("yuva-chat.js",scriptSrc||location.href).href);#root;#launcher=null;#badge=null;#panel;#controller=null;#messages={};#open=!1;#unread=0;#identity=null;#refused=!1;constructor(){super();this.#root=this.attachShadow({mode:"open"}),this.#panel=document.createElement("section"),this.#panel.className="panel",this.#panel.setAttribute("role","dialog"),this.#panel.hidden=!0,this.#panel.addEventListener("keydown",(event)=>{if(event.key==="Escape"&&this.layout==="launcher")this.close()})}get layout(){return this.getAttribute("layout")==="embedded"?"embedded":"launcher"}get channel(){return this.getAttribute("channel")}get server(){return(this.getAttribute("server")||(scriptSrc?new URL(scriptSrc).origin:location.origin)).replace(/\/+$/,"")}get locale(){return resolveLocale(this.getAttribute("locale"),navigator.language)}get isOpen(){return this.#open}get unread(){return this.#unread}setIdentityToken(source){if(this.#identity=source,this.#controller)this.#controller.then((controller)=>controller.identityChanged())}async signOut(){if(this.#identity=null,this.removeAttribute("identity-token"),this.#controller)await(await this.#controller).signOut()}async rate(conversationId,rating,comment){return(await this.#ensureController()).rate(conversationId,rating,comment)}connectedCallback(){this.#render();let channel=this.channel;if(this.layout==="embedded"||this.hasAttribute("open")||!channel)return;let stored=readStored(channel);if(stored.launcher)this.#applyLauncher(stored.launcher);let idle=()=>{if(!this.isConnected||this.#controller||this.channel!==channel)return;if(stored.session)this.#ensureController();else this.#styleLauncher(channel)};if("requestIdleCallback"in window)requestIdleCallback(idle,{timeout:3000});else setTimeout(idle,1000)}#styleLauncher(channel){fetch(`${this.server}/client/v1/channels/${encodeURIComponent(channel)}`,{credentials:"omit"}).then((response)=>{if(response.status===403||response.status===404)this.#refuse();return response.ok?response.json():null}).then((inbox)=>{if(!inbox||this.channel!==channel||this.#controller)return;let launcher=launcherOf(inbox);this.#applyLauncher(launcher),writeStored(channel,{...readStored(channel),launcher})}).catch(()=>{return})}disconnectedCallback(){let controller=this.#controller;if(this.#controller=null,controller)controller.then((c)=>c.destroy())}attributeChangedCallback(name,previous,value){if(!this.isConnected||previous===value)return;if(name==="open"){value===null?this.close():this.open();return}if(name==="identity-token"){if(this.#controller)this.#controller.then((controller)=>controller.identityChanged());return}if(name==="channel"||name==="server"){this.disconnectedCallback(),this.#refused=!1,this.#render();return}if(this.#render(),this.#controller)this.#controller.then((controller)=>controller.update())}async open(){if(this.#open||this.#refused)return;this.#open=!0,this.toggleAttribute("open-panel",!0),this.#syncLauncher();let controller=await this.#ensureController();if(!this.#open)return;this.#panel.hidden=!1,controller.opened()}close(){if(!this.#open||this.layout==="embedded")return;if(this.#open=!1,this.toggleAttribute("open-panel",!1),this.#panel.hidden=!0,this.#syncLauncher(),this.#launcher?.focus(),this.#controller)this.#controller.then((controller)=>controller.closed())}toggle(){return this.#open?this.close():this.open()}#config(){return{channel:this.channel,server:this.server,layout:this.layout,locale:this.locale}}#ensureController(){return this.#controller??=YuvaChatElement.loadPanel().then(({mount})=>mount({panel:this.#panel,config:()=>this.#config(),identityToken:async()=>this.#identity?await this.#identity()??null:this.getAttribute("identity-token"),isOpen:()=>this.#open,close:()=>this.close(),setUnread:(count)=>{this.#unread=count,this.#syncLauncher(),this.dispatchEvent(new CustomEvent("yuva-unread",{detail:{count}}))},setLauncher:(style)=>this.#applyLauncher(style),refused:()=>this.#refuse()})),this.#controller}#refuse(){if(this.#refused)return;this.#refused=!0,console.warn(`Yuva: channel ${this.channel} is unknown or does not allow ${location.origin}`),this.#open=!1,this.toggleAttribute("open-panel",!1),this.disconnectedCallback(),this.#render()}#applyLauncher(style){if(this.setAttribute("position",style.position==="left"?"left":"right"),style.color&&/^#[0-9a-f]{6}$/i.test(style.color))this.style.setProperty("--yuva-accent",style.color),this.style.setProperty("--yuva-on-accent",contrastOn(style.color))}#render(){let locale=this.locale;this.#messages=catalogFor(locale),this.setAttribute("lang",locale);let style=document.createElement("style");if(style.textContent=styles,this.#panel.dir=this.getAttribute("dir")??directionOf(locale),this.#panel.setAttribute("aria-modal","false"),this.#refused){this.#launcher=null,this.#badge=null,this.#root.replaceChildren();return}if(this.layout==="embedded"){this.#launcher=null,this.#badge=null,this.#root.replaceChildren(style,this.#panel),this.#open=!1,this.open();return}let launcher=document.createElement("button");if(launcher.type="button",launcher.className="launcher",launcher.addEventListener("click",()=>void this.toggle()),this.#launcher=launcher,this.#badge=document.createElement("span"),this.#badge.className="badge",this.#badge.setAttribute("aria-hidden","true"),this.#root.replaceChildren(style,this.#panel,launcher),this.#syncLauncher(),this.hasAttribute("open")&&!this.#open)this.open()}#syncLauncher(){let launcher=this.#launcher,badge=this.#badge;if(!launcher||!badge)return;let count=this.#unread;launcher.setAttribute("aria-expanded",String(this.#open)),launcher.setAttribute("aria-label",this.#open?translatePlain(this.#messages,{id:"pqZJT2"}):count>0?translatePlain(this.#messages,{id:"OkHvGB",values:{count}}):translatePlain(this.#messages,{id:"6I7cJR"})),launcher.innerHTML=this.#open?closeIcon:chatIcon,badge.textContent=count>9?"9+":String(count),badge.hidden=this.#open||count===0,launcher.append(badge)}}function define(){if(!customElements.get("yuva-chat"))customElements.define("yuva-chat",YuvaChatElement)}YuvaChatElement.loadPanel=()=>import("./yuva-chat-86e4mz63.js");define();export{YuvaChatElement,define};
109
+ `,chatIcon='<svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round" aria-hidden="true"><path d="M4 5h16v11H9l-5 4z"/></svg>',closeIcon='<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"/></svg>',scriptSrc=document.currentScript instanceof HTMLScriptElement?document.currentScript.src:"";class YuvaChatElement extends HTMLElement{static observedAttributes=["channel","server","layout","locale","dir","identity-token","open"];static loadPanel=()=>import(new URL("yuva-chat.js",scriptSrc||location.href).href);#root;#launcher=null;#badge=null;#panel;#controller=null;#messages={};#open=!1;#unread=0;#identity=null;#refused=!1;constructor(){super();this.#root=this.attachShadow({mode:"open"}),this.#panel=document.createElement("section"),this.#panel.className="panel",this.#panel.setAttribute("role","dialog"),this.#panel.hidden=!0,this.#panel.addEventListener("keydown",(event)=>{if(event.key==="Escape"&&this.layout==="launcher")this.close()})}get layout(){return this.getAttribute("layout")==="embedded"?"embedded":"launcher"}get channel(){return this.getAttribute("channel")}get server(){return(this.getAttribute("server")||(scriptSrc?new URL(scriptSrc).origin:location.origin)).replace(/\/+$/,"")}get locale(){return resolveLocale(this.getAttribute("locale"),navigator.language)}get isOpen(){return this.#open}get unread(){return this.#unread}setIdentityToken(source){if(this.#identity=source,this.#controller)this.#controller.then((controller)=>controller.identityChanged())}async signOut(){if(this.#identity=null,this.removeAttribute("identity-token"),this.#controller)await(await this.#controller).signOut()}async rate(conversationId,rating,comment){return(await this.#ensureController()).rate(conversationId,rating,comment)}connectedCallback(){this.#render();let channel=this.channel;if(this.layout==="embedded"||this.hasAttribute("open")||!channel)return;let stored=readStored(channel);if(stored.launcher)this.#applyLauncher(stored.launcher);let idle=()=>{if(!this.isConnected||this.#controller||this.channel!==channel)return;if(stored.session)this.#ensureController();else this.#styleLauncher(channel)};if("requestIdleCallback"in window)requestIdleCallback(idle,{timeout:3000});else setTimeout(idle,1000)}#styleLauncher(channel){fetch(`${this.server}/client/v1/channels/${encodeURIComponent(channel)}`,{credentials:"omit"}).then((response)=>{if(response.status===403||response.status===404)this.#refuse();return response.ok?response.json():null}).then((inbox)=>{if(!inbox||this.channel!==channel||this.#controller)return;let launcher=launcherOf(inbox);this.#applyLauncher(launcher),writeStored(channel,{...readStored(channel),launcher})}).catch(()=>{return})}disconnectedCallback(){let controller=this.#controller;if(this.#controller=null,controller)controller.then((c)=>c.destroy())}attributeChangedCallback(name,previous,value){if(!this.isConnected||previous===value)return;if(name==="open"){value===null?this.close():this.open();return}if(name==="identity-token"){if(this.#controller)this.#controller.then((controller)=>controller.identityChanged());return}if(name==="channel"||name==="server"){this.disconnectedCallback(),this.#refused=!1,this.#render();return}if(this.#render(),this.#controller)this.#controller.then((controller)=>controller.update())}async open(){if(this.#open||this.#refused)return;this.#open=!0,this.toggleAttribute("open-panel",!0),this.#syncLauncher();let controller=await this.#ensureController();if(!this.#open)return;if(this.#panel.hidden=!1,controller.opened(),this.layout!=="embedded")this.dispatchEvent(new Event("yuva-open",{bubbles:!0,composed:!0}))}close(){if(!this.#open||this.layout==="embedded")return;if(this.#open=!1,this.toggleAttribute("open-panel",!1),this.#panel.hidden=!0,this.#syncLauncher(),this.#launcher?.focus(),this.#controller)this.#controller.then((controller)=>controller.closed())}toggle(){return this.#open?this.close():this.open()}#config(){return{channel:this.channel,server:this.server,layout:this.layout,locale:this.locale}}#ensureController(){return this.#controller??=YuvaChatElement.loadPanel().then(({mount})=>mount({panel:this.#panel,config:()=>this.#config(),identityToken:async()=>this.#identity?await this.#identity()??null:this.getAttribute("identity-token"),isOpen:()=>this.#open,close:()=>this.close(),setUnread:(count)=>{this.#unread=count,this.#syncLauncher(),this.dispatchEvent(new CustomEvent("yuva-unread",{detail:{count}}))},setLauncher:(style)=>this.#applyLauncher(style),refused:()=>this.#refuse()})),this.#controller}#refuse(){if(this.#refused)return;this.#refused=!0,console.warn(`Yuva: channel ${this.channel} is unknown or does not allow ${location.origin}`),this.#open=!1,this.toggleAttribute("open-panel",!1),this.disconnectedCallback(),this.#render()}#applyLauncher(style){if(this.setAttribute("position",style.position==="left"?"left":"right"),style.color&&/^#[0-9a-f]{6}$/i.test(style.color))this.style.setProperty("--yuva-accent",style.color),this.style.setProperty("--yuva-on-accent",contrastOn(style.color))}#render(){let locale=this.locale;this.#messages=catalogFor(locale),this.setAttribute("lang",locale);let style=document.createElement("style");if(style.textContent=styles,this.#panel.dir=this.getAttribute("dir")??directionOf(locale),this.#panel.setAttribute("aria-modal","false"),this.#refused){this.#launcher=null,this.#badge=null,this.#root.replaceChildren();return}if(this.layout==="embedded"){this.#launcher=null,this.#badge=null,this.#root.replaceChildren(style,this.#panel),this.#open=!1,this.open();return}let launcher=document.createElement("button");if(launcher.type="button",launcher.className="launcher",launcher.addEventListener("click",()=>void this.toggle()),this.#launcher=launcher,this.#badge=document.createElement("span"),this.#badge.className="badge",this.#badge.setAttribute("aria-hidden","true"),this.#root.replaceChildren(style,this.#panel,launcher),this.#syncLauncher(),this.hasAttribute("open")&&!this.#open)this.open()}#syncLauncher(){let launcher=this.#launcher,badge=this.#badge;if(!launcher||!badge)return;let count=this.#unread;launcher.setAttribute("aria-expanded",String(this.#open)),launcher.setAttribute("aria-label",this.#open?translatePlain(this.#messages,{id:"pqZJT2"}):count>0?translatePlain(this.#messages,{id:"OkHvGB",values:{count}}):translatePlain(this.#messages,{id:"6I7cJR"})),launcher.innerHTML=this.#open?closeIcon:chatIcon,badge.textContent=count>9?"9+":String(count),badge.hidden=this.#open||count===0,launcher.append(badge)}}function define(){if(!customElements.get("yuva-chat"))customElements.define("yuva-chat",YuvaChatElement)}YuvaChatElement.loadPanel=()=>import("./yuva-chat-nkt3xjhb.js");define();export{YuvaChatElement,define};
@@ -0,0 +1 @@
1
+ import{storageKey2,readerStorageKey2,randomId2,YuvaClient2,createYuvaClient2}from"./index-geeh5h34.js";export{YuvaClient2 as YuvaClient,createYuvaClient2 as createYuvaClient,randomId2 as randomId,readerStorageKey2 as readerStorageKey,storageKey2 as storageKey};
package/dist/esm/docs.js CHANGED
@@ -1,4 +1,4 @@
1
- import{resolveLocale,directionOf,translatePlain,launcherOf,contrastOn,readStored,writeStored}from"./index-e2tek7yk.js";import{ApiError2,createYuvaClient2}from"./index-dj5dhqc1.js";import{el}from"./index-rdzxvj2r.js";var messages=JSON.parse(`{"-UxBoG":["Deleted member"],"1UzENP":["No"],"2lF8Zj":["Unanswered"],"5lWFkC":["Sign in"],"78dEKp":["Sort discussions"],"7oGBMv":["Discussions"],"8Ekoev":["You can't do that."],"8ntmc1":["Votes: ",["0"]],"D16wQE":["Could not open the discussion. Try again."],"D9CFV5":["Anonymous"],"E_qvzP":["Enter a valid e-mail address."],"EmiQvf":["Thanks! Anything we could add? (optional)"],"FMWGhb":["Deleted reader"],"G2fuEb":["Locked"],"GQVIIl":["You seem to be offline."],"GqCA_t":["Answered"],"Hw3P48":["Replies: ",["0"]],"JlFcis":["Send"],"KDw4GX":["Try again"],"KEjhwb":["This discussion is locked."],"KM6m8p":["Team"],"LwDe8g":["Sign in to take part in the discussion."],"MDmQKn":["Could not load the discussions."],"Pwqkdw":["Loading…"],"SOvBw9":["Was this page helpful?"],"Sad2tK":["Sending…"],"Su3Vyp":["Check the title and the text and try again."],"U7H5Fm":["Sorry about that. What was missing or unclear?"],"UbRKMZ":["Pending"],"WWy8TN":["Top"],"XJURKP":["E-mail address"],"a_qtPP":["E-mail for a reply (optional)"],"b6Orhy":["Have a question or an idea? Start the first one."],"b_f4xj":["Posts can be edited for 30 minutes after they are written."],"d6N994":["Thanks for your feedback."],"fMPkxb":["Show more"],"fcWrnU":["Sign out"],"isRobC":["New"],"j-3eim":["Discussions about this page"],"l75CjT":["Yes"],"lWv8id":["Awaiting approval"],"nLqMRA":["Team member"],"nW3go1":["Your browser blocked the sign-in window. Allow pop-ups for this site and try again."],"pzatAP":["Loading discussions…"],"rpfOwz":["No discussions about this page yet."],"tA4dAO":["No thanks"],"tAL5wg":["Could not send. Try again."],"tayT8P":["Discussions are turned off for this site."],"twEQ41":["Every discussion on this page has an answer."],"tzsBOP":["Too many attempts. Wait a moment and try again."],"uuIskV":["Sign in to write to us."],"xOTzt5":["just now"],"z1qf6l":["Start a discussion"],"zmA6sS":["Reader"]}`);var messages2=JSON.parse('{"-UxBoG":["Silinmiş üye"],"1UzENP":["Hayır"],"2lF8Zj":["Yanıtsız"],"5lWFkC":["Giriş yap"],"78dEKp":["Tartışmaları sırala"],"7oGBMv":["Tartışmalar"],"8Ekoev":["Bunu yapamazsınız."],"8ntmc1":["Oylar: ",["0"]],"D16wQE":["Tartışma açılamadı. Yeniden deneyin."],"D9CFV5":["Anonim"],"E_qvzP":["Geçerli bir e-posta adresi girin."],"EmiQvf":["Teşekkürler! Ekleyebileceğimiz bir şey var mı? (isteğe bağlı)"],"FMWGhb":["Silinmiş okuyucu"],"G2fuEb":["Kilitli"],"GQVIIl":["Çevrimdışı görünüyorsunuz."],"GqCA_t":["Yanıtlandı"],"Hw3P48":["Yanıtlar: ",["0"]],"JlFcis":["Gönder"],"KDw4GX":["Yeniden dene"],"KEjhwb":["Bu tartışma kilitli."],"KM6m8p":["Ekip"],"LwDe8g":["Tartışmaya katılmak için giriş yapın."],"MDmQKn":["Tartışmalar yüklenemedi."],"Pwqkdw":["Yükleniyor…"],"SOvBw9":["Bu sayfa işinize yaradı mı?"],"Sad2tK":["Gönderiliyor…"],"Su3Vyp":["Başlığı ve metni kontrol edip yeniden deneyin."],"U7H5Fm":["Üzgünüz. Eksik ya da anlaşılmayan ne vardı?"],"UbRKMZ":["Bekliyor"],"WWy8TN":["En çok oy alan"],"XJURKP":["E-posta adresi"],"a_qtPP":["Yanıt için e-posta (isteğe bağlı)"],"b6Orhy":["Bir sorunuz ya da fikriniz mi var? İlk tartışmayı siz başlatın."],"b_f4xj":["Gönderiler yazıldıktan sonra 30 dakika içinde düzenlenebilir."],"d6N994":["Geri bildiriminiz için teşekkürler."],"fMPkxb":["Daha fazla göster"],"fcWrnU":["Çıkış yap"],"isRobC":["Yeni"],"j-3eim":["Bu sayfayla ilgili tartışmalar"],"l75CjT":["Evet"],"lWv8id":["Onay bekliyor"],"nLqMRA":["Ekip üyesi"],"nW3go1":["Tarayıcınız giriş penceresini engelledi. Bu site için açılır pencerelere izin verip yeniden deneyin."],"pzatAP":["Tartışmalar yükleniyor…"],"rpfOwz":["Bu sayfayla ilgili henüz bir tartışma yok."],"tA4dAO":["Hayır, teşekkürler"],"tAL5wg":["Gönderilemedi. Yeniden deneyin."],"tayT8P":["Bu sitede tartışmalar kapalı."],"twEQ41":["Bu sayfadaki her tartışmanın bir yanıtı var."],"tzsBOP":["Çok fazla deneme. Biraz bekleyip tekrar deneyin."],"uuIskV":["Bize yazmak için oturum açın."],"xOTzt5":["az önce"],"z1qf6l":["Tartışma başlat"],"zmA6sS":["Okur"]}');var shared=Symbol.for("yuva.navigation");function navigation(){let win=window,target=win[shared];if(target)return target;let created=new EventTarget;target=win[shared]=created;let notify=()=>created.dispatchEvent(new Event("change"));for(let name of["pushState","replaceState"]){let original=history[name];history[name]=function(...args){let result=original.apply(this,args);return notify(),result}}return window.addEventListener("popstate",notify),window.addEventListener("hashchange",notify),target}function pageOf(value){try{let url=new URL(value||location.href,location.href);return url.search="",url.hash="",url.href}catch{return value||location.href}}function followPage(listener){let target=navigation(),scheduled=!1,changed=()=>{if(scheduled)return;scheduled=!0,queueMicrotask(()=>{scheduled=!1,listener()})};return target.addEventListener("change",changed),()=>target.removeEventListener("change",changed)}var catalogs={en:messages,tr:messages2},settings=new Map,Base=typeof HTMLElement>"u"?class{}:HTMLElement,scriptSrc=typeof document<"u"&&document.currentScript instanceof HTMLScriptElement?document.currentScript.src:"",baseStyles=`
1
+ import{createYuvaClient2}from"./index-geeh5h34.js";import{resolveLocale,translatePlain}from"./index-c6q8br7d.js";import{el}from"./index-rdzxvj2r.js";import{ApiError2}from"./index-gk84nm8f.js";import{directionOf,launcherOf,contrastOn,readStored,writeStored}from"./index-98r96a7e.js";var messages=JSON.parse(`{"-UxBoG":["Deleted member"],"1UzENP":["No"],"2lF8Zj":["Unanswered"],"5lWFkC":["Sign in"],"78dEKp":["Sort discussions"],"7oGBMv":["Discussions"],"8Ekoev":["You can't do that."],"8ntmc1":["Votes: ",["0"]],"D16wQE":["Could not open the discussion. Try again."],"D9CFV5":["Anonymous"],"E_qvzP":["Enter a valid e-mail address."],"EmiQvf":["Thanks! Anything we could add? (optional)"],"FMWGhb":["Deleted reader"],"G2fuEb":["Locked"],"GQVIIl":["You seem to be offline."],"GqCA_t":["Answered"],"Hw3P48":["Replies: ",["0"]],"JlFcis":["Send"],"KDw4GX":["Try again"],"KEjhwb":["This discussion is locked."],"KM6m8p":["Team"],"LwDe8g":["Sign in to take part in the discussion."],"MDmQKn":["Could not load the discussions."],"Pwqkdw":["Loading…"],"SOvBw9":["Was this page helpful?"],"Sad2tK":["Sending…"],"Su3Vyp":["Check the title and the text and try again."],"U7H5Fm":["Sorry about that. What was missing or unclear?"],"UbRKMZ":["Pending"],"WWy8TN":["Top"],"XJURKP":["E-mail address"],"a_qtPP":["E-mail for a reply (optional)"],"b6Orhy":["Have a question or an idea? Start the first one."],"b_f4xj":["Posts can be edited for 30 minutes after they are written."],"d6N994":["Thanks for your feedback."],"fMPkxb":["Show more"],"fcWrnU":["Sign out"],"isRobC":["New"],"j-3eim":["Discussions about this page"],"l75CjT":["Yes"],"lWv8id":["Awaiting approval"],"nLqMRA":["Team member"],"nW3go1":["Your browser blocked the sign-in window. Allow pop-ups for this site and try again."],"pzatAP":["Loading discussions…"],"rpfOwz":["No discussions about this page yet."],"tA4dAO":["No thanks"],"tAL5wg":["Could not send. Try again."],"tayT8P":["Discussions are turned off for this site."],"twEQ41":["Every discussion on this page has an answer."],"tzsBOP":["Too many attempts. Wait a moment and try again."],"uuIskV":["Sign in to write to us."],"xOTzt5":["just now"],"z1qf6l":["Start a discussion"],"zmA6sS":["Reader"]}`);var messages2=JSON.parse('{"-UxBoG":["Gelöschtes Mitglied"],"1UzENP":["Nein"],"2lF8Zj":["Unbeantwortet"],"5lWFkC":["Anmelden"],"78dEKp":["Diskussionen sortieren"],"7oGBMv":["Diskussionen"],"8Ekoev":["Das können Sie nicht tun."],"8ntmc1":["Stimmen: ",["0"]],"D16wQE":["Die Diskussion konnte nicht eröffnet werden. Versuchen Sie es erneut."],"D9CFV5":["Anonym"],"E_qvzP":["Geben Sie eine gültige E-Mail-Adresse ein."],"EmiQvf":["Danke! Können wir noch etwas ergänzen? (optional)"],"FMWGhb":["Gelöschter Leser"],"G2fuEb":["Gesperrt"],"GQVIIl":["Sie scheinen offline zu sein."],"GqCA_t":["Beantwortet"],"Hw3P48":["Antworten: ",["0"]],"JlFcis":["Senden"],"KDw4GX":["Erneut versuchen"],"KEjhwb":["Diese Diskussion ist gesperrt."],"KM6m8p":["Team"],"LwDe8g":["Melden Sie sich an, um an der Diskussion teilzunehmen."],"MDmQKn":["Die Diskussionen konnten nicht geladen werden."],"Pwqkdw":["Wird geladen…"],"SOvBw9":["War diese Seite hilfreich?"],"Sad2tK":["Wird gesendet…"],"Su3Vyp":["Prüfen Sie Titel und Text und versuchen Sie es erneut."],"U7H5Fm":["Das tut uns leid. Was hat gefehlt oder war unklar?"],"UbRKMZ":["Ausstehend"],"WWy8TN":["Beliebteste"],"XJURKP":["E-Mail-Adresse"],"a_qtPP":["E-Mail für eine Antwort (optional)"],"b6Orhy":["Haben Sie eine Frage oder eine Idee? Starten Sie die erste Diskussion."],"b_f4xj":["Beiträge können bis 30 Minuten nach dem Schreiben bearbeitet werden."],"d6N994":["Danke für Ihr Feedback."],"fMPkxb":["Mehr anzeigen"],"fcWrnU":["Abmelden"],"isRobC":["Neu"],"j-3eim":["Diskussionen zu dieser Seite"],"l75CjT":["Ja"],"lWv8id":["Wartet auf Freigabe"],"nLqMRA":["Teammitglied"],"nW3go1":["Ihr Browser hat das Anmeldefenster blockiert. Erlauben Sie Pop-ups für diese Website und versuchen Sie es erneut."],"pzatAP":["Diskussionen werden geladen…"],"rpfOwz":["Zu dieser Seite gibt es noch keine Diskussionen."],"tA4dAO":["Nein, danke"],"tAL5wg":["Senden fehlgeschlagen. Versuchen Sie es erneut."],"tayT8P":["Diskussionen sind für diese Website deaktiviert."],"twEQ41":["Jede Diskussion auf dieser Seite hat eine Antwort."],"tzsBOP":["Zu viele Versuche. Warten Sie einen Moment und versuchen Sie es erneut."],"uuIskV":["Melden Sie sich an, um uns zu schreiben."],"xOTzt5":["gerade eben"],"z1qf6l":["Diskussion starten"],"zmA6sS":["Leser"]}');var messages3=JSON.parse('{"-UxBoG":["Silinmiş üye"],"1UzENP":["Hayır"],"2lF8Zj":["Yanıtsız"],"5lWFkC":["Giriş yap"],"78dEKp":["Tartışmaları sırala"],"7oGBMv":["Tartışmalar"],"8Ekoev":["Bunu yapamazsınız."],"8ntmc1":["Oylar: ",["0"]],"D16wQE":["Tartışma açılamadı. Yeniden deneyin."],"D9CFV5":["Anonim"],"E_qvzP":["Geçerli bir e-posta adresi girin."],"EmiQvf":["Teşekkürler! Ekleyebileceğimiz bir şey var mı? (isteğe bağlı)"],"FMWGhb":["Silinmiş okuyucu"],"G2fuEb":["Kilitli"],"GQVIIl":["Çevrimdışı görünüyorsunuz."],"GqCA_t":["Yanıtlandı"],"Hw3P48":["Yanıtlar: ",["0"]],"JlFcis":["Gönder"],"KDw4GX":["Yeniden dene"],"KEjhwb":["Bu tartışma kilitli."],"KM6m8p":["Ekip"],"LwDe8g":["Tartışmaya katılmak için giriş yapın."],"MDmQKn":["Tartışmalar yüklenemedi."],"Pwqkdw":["Yükleniyor…"],"SOvBw9":["Bu sayfa işinize yaradı mı?"],"Sad2tK":["Gönderiliyor…"],"Su3Vyp":["Başlığı ve metni kontrol edip yeniden deneyin."],"U7H5Fm":["Üzgünüz. Eksik ya da anlaşılmayan ne vardı?"],"UbRKMZ":["Bekliyor"],"WWy8TN":["En çok oy alan"],"XJURKP":["E-posta adresi"],"a_qtPP":["Yanıt için e-posta (isteğe bağlı)"],"b6Orhy":["Bir sorunuz ya da fikriniz mi var? İlk tartışmayı siz başlatın."],"b_f4xj":["Gönderiler yazıldıktan sonra 30 dakika içinde düzenlenebilir."],"d6N994":["Geri bildiriminiz için teşekkürler."],"fMPkxb":["Daha fazla göster"],"fcWrnU":["Çıkış yap"],"isRobC":["Yeni"],"j-3eim":["Bu sayfayla ilgili tartışmalar"],"l75CjT":["Evet"],"lWv8id":["Onay bekliyor"],"nLqMRA":["Ekip üyesi"],"nW3go1":["Tarayıcınız giriş penceresini engelledi. Bu site için açılır pencerelere izin verip yeniden deneyin."],"pzatAP":["Tartışmalar yükleniyor…"],"rpfOwz":["Bu sayfayla ilgili henüz bir tartışma yok."],"tA4dAO":["Hayır, teşekkürler"],"tAL5wg":["Gönderilemedi. Yeniden deneyin."],"tayT8P":["Bu sitede tartışmalar kapalı."],"twEQ41":["Bu sayfadaki her tartışmanın bir yanıtı var."],"tzsBOP":["Çok fazla deneme. Biraz bekleyip tekrar deneyin."],"uuIskV":["Bize yazmak için oturum açın."],"xOTzt5":["az önce"],"z1qf6l":["Tartışma başlat"],"zmA6sS":["Okur"]}');var shared=Symbol.for("yuva.navigation");function navigation(){let win=window,target=win[shared];if(target)return target;let created=new EventTarget;target=win[shared]=created;let notify=()=>created.dispatchEvent(new Event("change"));for(let name of["pushState","replaceState"]){let original=history[name];history[name]=function(...args){let result=original.apply(this,args);return notify(),result}}return window.addEventListener("popstate",notify),window.addEventListener("hashchange",notify),target}function pageOf(value){try{let url=new URL(value||location.href,location.href);return url.search="",url.hash="",url.href}catch{return value||location.href}}function followPage(listener){let target=navigation(),scheduled=!1,changed=()=>{if(scheduled)return;scheduled=!0,queueMicrotask(()=>{scheduled=!1,listener()})};return target.addEventListener("change",changed),()=>target.removeEventListener("change",changed)}var catalogs={en:messages,tr:messages3,de:messages2},settings=new Map,Base=typeof HTMLElement>"u"?class{}:HTMLElement,scriptSrc=typeof document<"u"&&document.currentScript instanceof HTMLScriptElement?document.currentScript.src:"",baseStyles=`
2
2
  :host {
3
3
  --yuva-accent: #2563eb;
4
4
  --yuva-on-accent: #ffffff;
@@ -131,4 +131,4 @@ button { font: inherit; color: inherit; }
131
131
  .choice[aria-pressed="true"] { background: var(--yuva-soft); }
132
132
  .choice:disabled { cursor: default; }
133
133
  .choice:focus-visible { outline: 2px solid var(--yuva-accent); outline-offset: 2px; }
134
- `;function storageKey(channel){return`yuva:${channel}:page-ratings`}function remembered(channel){try{let raw=localStorage.getItem(storageKey(channel));return raw?JSON.parse(raw):{}}catch{return{}}}function remember(channel,page,rating){try{let ratings=remembered(channel);delete ratings[page],ratings[page]=rating;let pages=Object.keys(ratings);for(let old of pages.slice(0,Math.max(0,pages.length-maxRemembered)))delete ratings[old];localStorage.setItem(storageKey(channel),JSON.stringify(ratings))}catch{}}class YuvaPageFeedbackElement extends YuvaDocsElement{#phase="ask";#rating;#error="";#comment=el("textarea");#email=el("input");constructor(){super();this.#comment.rows=3,this.#comment.maxLength=5000,this.#comment.dir="auto",this.#comment.addEventListener("input",()=>this.#syncSend()),this.#email.type="email",this.#email.autocomplete="email"}get rating(){return this.#rating??null}reset(){this.#phase="ask",this.#error="",this.#comment.value="",this.#rating=this.channel?remembered(this.channel)[this.pageUrl]:void 0}pageChanged(){if(this.#phase==="ask"&&!this.#error)this.#rating=this.channel?remembered(this.channel)[this.pageUrl]:void 0;this.render()}async rate(rating){let channel=this.channel;if(!channel||this.refused||this.#phase==="rating"||this.#phase==="sending")return;let page=this.pageUrl,previous=remembered(channel)[page];if(this.#error="",previous===rating){this.#rating=rating,this.#phase=this.canWrite?"form":"thanks",this.render(),this.#comment.focus({preventScroll:!0});return}this.#phase="rating",this.#rating=rating,this.render();try{await this.client().ratePage({page,title:this.pageTitle||void 0,rating,previous})}catch(error){if(this.pageUrl!==page||this.refusal(error))return;this.#rating=previous,this.#phase="ask",this.#error=this.errorText(error),this.render();return}if(remember(channel,page,rating),this.emit("yuva-rating",{page,rating,previous:previous??null}),this.pageUrl!==page)return;this.#phase=this.canWrite?"form":"thanks",this.render(),this.#comment.focus({preventScroll:!0})}async#send(){let body=this.#comment.value.trim(),email=this.#email.value.trim(),rating=this.#rating;if(!body||this.#phase!=="form")return;if(email&&!this.#email.checkValidity()){this.#error=this.t({id:"E_qvzP"}),this.render();return}let page=this.pageUrl;this.#phase="sending",this.#error="",this.render();try{let{conversation}=await this.client().sendFeedback({page_url:page,page_title:this.pageTitle||void 0,rating,body,locale:this.locale,allow_email:this.identified||!!email,email:!this.identified&&email?email:void 0});if(this.emit("yuva-feedback",{page,rating:rating??null,conversation_id:conversation.id}),this.pageUrl!==page)return;this.#comment.value="",this.#phase="thanks"}catch(error){if(this.pageUrl!==page||this.refusal(error,!1))return;this.#phase="form",this.#error=this.errorText(error)}this.render()}#syncSend(){let send=this.root.querySelector(".send");if(send)send.disabled=this.#phase!=="form"||this.#comment.value.trim()===""}render(){if(this.refused||!this.channel){this.root.replaceChildren();return}let question=this.t({id:"SOvBw9"}),ask=el("div","ask");ask.setAttribute("role","group"),ask.setAttribute("aria-label",question);let choices=el("div","choices");for(let value of["up","down"]){let button=el("button","choice");button.type="button",button.innerHTML=thumb(value==="down"),button.append(el("span","",value==="up"?this.t({id:"l75CjT"}):this.t({id:"1UzENP"}))),button.setAttribute("aria-pressed",String(this.#rating===value)),button.disabled=this.#phase==="rating"||this.#phase==="sending",button.addEventListener("click",()=>void this.rate(value)),choices.append(button)}ask.append(el("p","question",question),choices);let children=[ask];if(this.#phase==="form"||this.#phase==="sending"){let form=el("form");form.noValidate=!0,form.addEventListener("submit",(event)=>{event.preventDefault(),this.#send()});let prompt=this.#rating==="down"?this.t({id:"U7H5Fm"}):this.t({id:"EmiQvf"});if(this.#comment.placeholder=prompt,this.#comment.setAttribute("aria-label",prompt),this.#comment.disabled=this.#phase==="sending",form.append(this.#comment),!this.identified)this.#email.placeholder=this.t({id:"a_qtPP"}),this.#email.setAttribute("aria-label",this.t({id:"XJURKP"})),this.#email.disabled=this.#phase==="sending",form.append(this.#email);let skip=el("button","link",this.t({id:"tA4dAO"}));skip.type="button",skip.disabled=this.#phase==="sending",skip.addEventListener("click",()=>{this.#phase="thanks",this.#error="",this.render()});let send=el("button","send",this.#phase==="sending"?this.t({id:"Sad2tK"}):this.t({id:"JlFcis"}));send.type="submit";let actions=el("div","actions");actions.append(skip,send),form.append(actions),children.push(form)}else if(this.#phase==="thanks"){let thanks=el("p","muted",this.t({id:"d6N994"}));thanks.setAttribute("role","status"),children.push(thanks)}if(this.#error){let error=el("p","error",this.#error);error.setAttribute("role","alert"),children.push(error)}this.frame(styles2,...children),this.#syncSend()}}function defineDocs(){if(typeof customElements>"u")return;if(!customElements.get("yuva-discussions"))customElements.define("yuva-discussions",YuvaDiscussionsElement);if(!customElements.get("yuva-page-feedback"))customElements.define("yuva-page-feedback",YuvaPageFeedbackElement)}YuvaDiscussionsElement.loadThread=()=>import("./thread-3ydc3xfc.js");defineDocs();export{YuvaDiscussionsElement,YuvaPageFeedbackElement,defineDocs};
134
+ `;function storageKey(channel){return`yuva:${channel}:page-ratings`}function remembered(channel){try{let raw=localStorage.getItem(storageKey(channel));return raw?JSON.parse(raw):{}}catch{return{}}}function remember(channel,page,rating){try{let ratings=remembered(channel);delete ratings[page],ratings[page]=rating;let pages=Object.keys(ratings);for(let old of pages.slice(0,Math.max(0,pages.length-maxRemembered)))delete ratings[old];localStorage.setItem(storageKey(channel),JSON.stringify(ratings))}catch{}}class YuvaPageFeedbackElement extends YuvaDocsElement{#phase="ask";#rating;#error="";#comment=el("textarea");#email=el("input");constructor(){super();this.#comment.rows=3,this.#comment.maxLength=5000,this.#comment.dir="auto",this.#comment.addEventListener("input",()=>this.#syncSend()),this.#email.type="email",this.#email.autocomplete="email"}get rating(){return this.#rating??null}reset(){this.#phase="ask",this.#error="",this.#comment.value="",this.#rating=this.channel?remembered(this.channel)[this.pageUrl]:void 0}pageChanged(){if(this.#phase==="ask"&&!this.#error)this.#rating=this.channel?remembered(this.channel)[this.pageUrl]:void 0;this.render()}async rate(rating){let channel=this.channel;if(!channel||this.refused||this.#phase==="rating"||this.#phase==="sending")return;let page=this.pageUrl,previous=remembered(channel)[page];if(this.#error="",previous===rating){this.#rating=rating,this.#phase=this.canWrite?"form":"thanks",this.render(),this.#comment.focus({preventScroll:!0});return}this.#phase="rating",this.#rating=rating,this.render();try{await this.client().ratePage({page,title:this.pageTitle||void 0,rating,previous})}catch(error){if(this.pageUrl!==page||this.refusal(error))return;this.#rating=previous,this.#phase="ask",this.#error=this.errorText(error),this.render();return}if(remember(channel,page,rating),this.emit("yuva-rating",{page,rating,previous:previous??null}),this.pageUrl!==page)return;this.#phase=this.canWrite?"form":"thanks",this.render(),this.#comment.focus({preventScroll:!0})}async#send(){let body=this.#comment.value.trim(),email=this.#email.value.trim(),rating=this.#rating;if(!body||this.#phase!=="form")return;if(email&&!this.#email.checkValidity()){this.#error=this.t({id:"E_qvzP"}),this.render();return}let page=this.pageUrl;this.#phase="sending",this.#error="",this.render();try{let{conversation}=await this.client().sendFeedback({page_url:page,page_title:this.pageTitle||void 0,rating,body,locale:this.locale,allow_email:this.identified||!!email,email:!this.identified&&email?email:void 0});if(this.emit("yuva-feedback",{page,rating:rating??null,conversation_id:conversation.id}),this.pageUrl!==page)return;this.#comment.value="",this.#phase="thanks"}catch(error){if(this.pageUrl!==page||this.refusal(error,!1))return;this.#phase="form",this.#error=this.errorText(error)}this.render()}#syncSend(){let send=this.root.querySelector(".send");if(send)send.disabled=this.#phase!=="form"||this.#comment.value.trim()===""}render(){if(this.refused||!this.channel){this.root.replaceChildren();return}let question=this.t({id:"SOvBw9"}),ask=el("div","ask");ask.setAttribute("role","group"),ask.setAttribute("aria-label",question);let choices=el("div","choices");for(let value of["up","down"]){let button=el("button","choice");button.type="button",button.innerHTML=thumb(value==="down"),button.append(el("span","",value==="up"?this.t({id:"l75CjT"}):this.t({id:"1UzENP"}))),button.setAttribute("aria-pressed",String(this.#rating===value)),button.disabled=this.#phase==="rating"||this.#phase==="sending",button.addEventListener("click",()=>void this.rate(value)),choices.append(button)}ask.append(el("p","question",question),choices);let children=[ask];if(this.#phase==="form"||this.#phase==="sending"){let form=el("form");form.noValidate=!0,form.addEventListener("submit",(event)=>{event.preventDefault(),this.#send()});let prompt=this.#rating==="down"?this.t({id:"U7H5Fm"}):this.t({id:"EmiQvf"});if(this.#comment.placeholder=prompt,this.#comment.setAttribute("aria-label",prompt),this.#comment.disabled=this.#phase==="sending",form.append(this.#comment),!this.identified)this.#email.placeholder=this.t({id:"a_qtPP"}),this.#email.setAttribute("aria-label",this.t({id:"XJURKP"})),this.#email.disabled=this.#phase==="sending",form.append(this.#email);let skip=el("button","link",this.t({id:"tA4dAO"}));skip.type="button",skip.disabled=this.#phase==="sending",skip.addEventListener("click",()=>{this.#phase="thanks",this.#error="",this.render()});let send=el("button","send",this.#phase==="sending"?this.t({id:"Sad2tK"}):this.t({id:"JlFcis"}));send.type="submit";let actions=el("div","actions");actions.append(skip,send),form.append(actions),children.push(form)}else if(this.#phase==="thanks"){let thanks=el("p","muted",this.t({id:"d6N994"}));thanks.setAttribute("role","status"),children.push(thanks)}if(this.#error){let error=el("p","error",this.#error);error.setAttribute("role","alert"),children.push(error)}this.frame(styles2,...children),this.#syncSend()}}function defineDocs(){if(typeof customElements>"u")return;if(!customElements.get("yuva-discussions"))customElements.define("yuva-discussions",YuvaDiscussionsElement);if(!customElements.get("yuva-page-feedback"))customElements.define("yuva-page-feedback",YuvaPageFeedbackElement)}YuvaDiscussionsElement.loadThread=()=>import("./thread-btm71nxt.js");defineDocs();export{YuvaDiscussionsElement,YuvaPageFeedbackElement,defineDocs};
@@ -0,0 +1,2 @@
1
+ function translate(messages,descriptor,fallback){let entry=messages[descriptor.id]??fallback[descriptor.id],values=descriptor.values??{};if(typeof entry==="string")return entry;if(Array.isArray(entry))return entry.map((part)=>typeof part==="string"?part:Array.isArray(part)?String(values[part[0]]??""):"").join("");return descriptor.message??descriptor.id}var rtlLanguages=new Set(["ar","fa","he","ur"]),sourceLocale="en";function localeIn(catalogs,candidates){for(let candidate of candidates){if(!candidate)continue;let tag=candidate.toLowerCase();if(catalogs[tag])return tag;let language=tag.split(/[-_]/)[0]??"";if(catalogs[language])return language}return sourceLocale}function directionOf(locale){return rtlLanguages.has(locale.split(/[-_]/)[0]??"")?"rtl":"ltr"}function launcherOf(inbox){return{position:inbox.chat.launcher_position,color:inbox.chat.launcher_color??inbox.branding.color}}function contrastOn(color){let n=Number.parseInt(color.slice(1),16),[r,g,b]=[n>>16&255,n>>8&255,n&255].map((c)=>{let s=c/255;return s<=0.03928?s/12.92:((s+0.055)/1.055)**2.4});return 0.2126*r+0.7152*g+0.0722*b>0.4?"#111827":"#ffffff"}function storageKey(channel){return`yuva:${channel}`}function readStored(channel){try{let raw=localStorage.getItem(storageKey(channel));return raw?JSON.parse(raw):{}}catch{return{}}}function writeStored(channel,state){try{localStorage.setItem(storageKey(channel),JSON.stringify(state))}catch{}}
2
+ export{translate,localeIn,directionOf,launcherOf,contrastOn,readStored,writeStored};
@@ -0,0 +1,2 @@
1
+ import{translate,localeIn}from"./index-98r96a7e.js";var messages=JSON.parse('{"6I7cJR":["Open chat"],"OkHvGB":["Open chat, ",["count"]," unread"],"pqZJT2":["Close chat"]}');var messages2=JSON.parse('{"6I7cJR":["Chat öffnen"],"OkHvGB":["Chat öffnen, ",["count"]," ungelesen"],"pqZJT2":["Chat schließen"]}');var messages3=JSON.parse('{"6I7cJR":["Sohbeti aç"],"OkHvGB":["Sohbeti aç, ",["count"]," okunmamış"],"pqZJT2":["Sohbeti kapat"]}');var catalogs={en:messages,tr:messages3,de:messages2};function resolveLocale(...candidates){return localeIn(catalogs,candidates)}function catalogFor(locale){return catalogs[locale]??messages}function translatePlain(messages2,descriptor,fallback=messages){return translate(messages2,descriptor,fallback)}
2
+ export{resolveLocale,catalogFor,translatePlain};
@@ -0,0 +1,12 @@
1
+ import{ApiError2,call,mailingLists,createSubscription}from"./index-gk84nm8f.js";class Realtime{server;socketClass;handlers;#socket=null;#lastEventId=0;#attempt=0;#timer=null;#stopped=!0;constructor(server,socketClass,handlers){this.server=server;this.socketClass=socketClass;this.handlers=handlers}get connected(){return this.#socket?.readyState===1}start(){if(this.#stopped=!1,!this.#socket&&!this.#timer)this.#connect()}stop(){if(this.#stopped=!0,this.#timer)clearTimeout(this.#timer);this.#timer=null;let socket=this.#socket;this.#socket=null,socket?.close(1000)}reset(){this.#lastEventId=0}nudge(){if(this.#stopped||this.#socket)return;if(this.#timer)clearTimeout(this.#timer);this.#timer=null,this.#connect()}#connect(){let token=this.handlers.token();if(!token||this.#stopped)return;let url=new URL(`${this.server}/client/v1/realtime`,globalThis.location?.href);if(url.protocol=url.protocol==="https:"?"wss:":"ws:",this.#lastEventId>0)url.searchParams.set("last_event_id",String(this.#lastEventId));let socket;try{socket=new this.socketClass(url,["yuva",`yuva.token.${token}`])}catch{this.#schedule();return}this.#socket=socket,socket.onmessage=(event)=>{let message;try{message=JSON.parse(String(event.data))}catch{return}if(message.type==="ready")this.#attempt=0,this.#lastEventId=Math.max(this.#lastEventId,message.last_event_id);else if("id"in message&&typeof message.id==="number")this.#lastEventId=Math.max(this.#lastEventId,message.id);this.handlers.event(message)},socket.onclose=(event)=>{if(this.#socket!==socket)return;if(this.#socket=null,this.#stopped)return;if(event.code===1008){this.#lastEventId=0,this.handlers.sessionEnded();return}this.#schedule()}}#schedule(){if(this.#stopped||this.#timer)return;let attempt=this.#attempt++;this.handlers.reconnecting(attempt);let delay=Math.min(30000,1000*2**attempt)*(0.75+Math.random()*0.5);this.#timer=setTimeout(()=>{this.#timer=null,this.#connect()},delay)}}function readerConnectUrl2(server,channel,origin){let url=new URL("/account/connect",server);return url.searchParams.set("channel",channel),url.searchParams.set("origin",origin),url.href}function isReaderToken(value){let data=value;return!!data&&typeof data.token==="string"&&data.token.startsWith("yuva_rd_")&&typeof data.expires_at==="string"&&!Number.isNaN(Date.parse(data.expires_at))&&typeof data.channel_key==="string"&&typeof data.origin==="string"&&!!data.reader&&typeof data.reader.initials==="string"}function openReaderPopup2(server,channel,{width=460,height=640}={}){let serverOrigin=new URL(server).origin,origin=location.origin,left=Math.max(0,(window.screenX??0)+((window.outerWidth??width)-width)/2),top=Math.max(0,(window.screenY??0)+((window.outerHeight??height)-height)/2),popup=window.open(readerConnectUrl2(server,channel,origin),"yuva-reader",`popup=yes,width=${width},height=${height},left=${Math.round(left)},top=${Math.round(top)}`);if(!popup)return Promise.reject(new ApiError2(0,"popup_blocked"));return new Promise((resolve,reject)=>{let done=!1,finish=()=>{done=!0,window.removeEventListener("message",received),clearInterval(watch)},received=(event)=>{if(done||event.origin!==serverOrigin||event.source&&event.source!==popup)return;let data=event.data;if(!isReaderToken(data)||data.channel_key!==channel||data.origin!==origin)return;finish();try{popup.close()}catch{}resolve({token:data.token,expires_at:data.expires_at,reader:data.reader})},watch=setInterval(()=>{let closed=!1;try{closed=popup.closed}catch{}if(!closed)return;clearInterval(watch),setTimeout(()=>{if(done)return;finish(),reject(new ApiError2(0,"sign_in_cancelled"))},300)},500);window.addEventListener("message",received)})}function storageKey2(channel){return`yuva:${channel}`}function readerStorageKey2(server,channel){return`yuva:reader:${server} ${channel}`}function randomId2(){if(typeof crypto.randomUUID==="function")return crypto.randomUUID();return Array.from(crypto.getRandomValues(new Uint8Array(16)),(b)=>b.toString(16).padStart(2,"0")).join("")}function jwtSubject(token){try{let part=(token.split(".")[1]??"").replace(/-/g,"+").replace(/_/g,"/"),payload=JSON.parse(atob(part));return typeof payload.sub==="string"?payload.sub:null}catch{return null}}function defaultStorage(){try{let storage=globalThis.localStorage;if(storage)return storage}catch{}let memory=new Map;return{getItem:(key)=>memory.get(key)??null,setItem:(key,value)=>void memory.set(key,value)}}function query(params){let search=new URLSearchParams;for(let[name,value]of Object.entries(params))if(value!==void 0)search.set(name,String(value));let text=search.toString();return text?`?${text}`:""}function quoted(value){return value.replace(/"/g,"%22").replace(/\r/g,"%0D").replace(/\n/g,"%0A")}function multipart(fields,files,boundary){let parts=[];for(let[name,value]of fields)parts.push(`--${boundary}\r
2
+ Content-Disposition: form-data; name="${name}"\r
3
+ \r
4
+ ${value}\r
5
+ `);for(let file of files){let filename=file.name||"file";parts.push(`--${boundary}\r
6
+ Content-Disposition: form-data; name="files"; filename="${quoted(filename)}"\r
7
+ Content-Type: ${file.type||"application/octet-stream"}\r
8
+ \r
9
+ `,file,`\r
10
+ `)}return parts.push(`--${boundary}--\r
11
+ `),new Blob(parts)}class YuvaClient2{server;channel;#fetch;#socketClass;#storage;#identity;#idempotencyKeys;#token=null;#session=null;#starting=null;#renewing=null;#realtime=null;#reader;#handlers=new Map;constructor(options){this.server=options.server.replace(/\/+$/,""),this.channel=options.channel,this.#fetch=options.fetch??((url,init)=>fetch(url,init)),this.#socketClass=options.WebSocket??globalThis.WebSocket,this.#storage=options.storage??defaultStorage(),this.#identity=options.identityToken??null,this.#idempotencyKeys=options.idempotencyKeys??typeof globalThis.document>"u"}get session(){return this.#session}get token(){return this.#token}get connected(){return this.#realtime?.connected??!1}on(type,handler){let set=this.#handlers.get(type);if(!set)this.#handlers.set(type,set=new Set);return set.add(handler),()=>void set.delete(handler)}#emit(type,value){for(let handler of this.#handlers.get(type)??[])handler(value)}stored(){try{let raw=this.#storage.getItem(storageKey2(this.channel));return raw?JSON.parse(raw):{}}catch{return{}}}#store(state){try{this.#storage.setItem(storageKey2(this.channel),JSON.stringify(state))}catch{}}channelSettings(){return this.#call(`/client/v1/channels/${encodeURIComponent(this.channel)}`)}ratePage(input){return this.#call(`/client/v1/channels/${encodeURIComponent(this.channel)}/page-ratings`,{method:"POST",json:input})}lists(){return mailingLists(this.#fetch,this.server,this.channel)}subscribe(input){return createSubscription(this.#fetch,this.server,this.channel,input)}subscriptions(){return this.#authed("/client/v1/subscriptions")}subscribeList(listId,input){return this.#authed(`/client/v1/subscriptions/${encodeURIComponent(listId)}`,{method:"PUT",json:input})}unsubscribeList(listId){return this.#authed(`/client/v1/subscriptions/${encodeURIComponent(listId)}`,{method:"DELETE"})}setIdentityToken(source){this.#identity=source,this.reset()}start(){if(this.#session)return Promise.resolve(this.#session);return this.#starting??=this.#open(!1).then((session)=>(this.#emit("session",{session,renewed:!1}),session)).finally(()=>{this.#starting=null}),this.#starting}async refresh(){let info=await this.#authed("/client/v1/session");return this.#session={...this.#session,...info},this.#emit("session",{session:this.#session,renewed:!1}),this.#session}reset(){this.#realtime?.stop(),this.#realtime?.reset(),this.#starting=null,this.#renewing=null,this.#token=null,this.#session=null}async signOut(){let token=this.#token;this.reset();let stored=this.stored();if(delete stored.session,this.#store(stored),token)await this.#call("/client/v1/session",{method:"DELETE",token}).catch(()=>{return})}async connect(){if(!this.#socketClass)throw Error("Yuva: no WebSocket in this runtime; pass options.WebSocket");if(await this.start(),this.#realtime??=new Realtime(this.server,this.#socketClass,{token:()=>this.#token,event:(message)=>{if(message.type==="ready")this.#emit("connection",{state:"open",attempt:0});this.#emit("event",message)},sessionEnded:()=>void this.#renew().catch(()=>this.#realtime?.nudge()),reconnecting:(attempt)=>{if(this.#emit("connection",{state:"reconnecting",attempt}),attempt===2)this.#authed("/client/v1/session").catch(()=>{return})}}),!this.#realtime.connected)this.#emit("connection",{state:"connecting",attempt:0});this.#realtime.start()}disconnect(){this.#realtime?.stop(),this.#emit("connection",{state:"closed",attempt:0})}reconnect(){this.#realtime?.nudge()}listConversations(page={}){return this.#authed(`/client/v1/conversations${query({...page})}`)}getConversation(id){return this.#authed(`/client/v1/conversations/${encodeURIComponent(id)}`)}startConversation(input){return this.#authed("/client/v1/conversations",this.#messageRequest(input))}listMessages(conversationId,page={}){return this.#authed(`/client/v1/conversations/${encodeURIComponent(conversationId)}/messages${query({...page})}`)}sendMessage(conversationId,input){return this.#authed(`/client/v1/conversations/${encodeURIComponent(conversationId)}/messages`,this.#messageRequest(input))}markRead(conversationId,messageId){return this.#authed(`/client/v1/conversations/${encodeURIComponent(conversationId)}/read`,{method:"POST",json:messageId?{message_id:messageId}:{}})}rate(conversationId,rating,comment){let body={rating,comment:comment?.trim()||void 0};return this.#authed(`/client/v1/conversations/${encodeURIComponent(conversationId)}/rating`,{method:"POST",json:body})}setTyping(conversationId,typing){return this.#authed(`/client/v1/conversations/${encodeURIComponent(conversationId)}/typing`,{method:"POST",json:{typing}})}setEmail(email){return this.#authed("/client/v1/contact/email",{method:"PUT",json:{email}})}attachment(id){return this.#authed(`/client/v1/attachments/${encodeURIComponent(id)}`,{blob:!0})}sendFeedback(input){let client_id=input.client_id??randomId2();return this.#authed("/client/v1/feedback",{method:"POST",headers:this.#idempotency(client_id),json:{...input,client_id}})}get reader(){if(this.#reader===void 0)try{let raw=this.#storage.getItem(readerStorageKey2(this.server,this.channel));this.#reader=raw?JSON.parse(raw):null}catch{this.#reader=null}if(this.#reader&&Date.parse(this.#reader.expires_at)<=Date.now()+30000)this.#setReader(null);return this.#reader}#setReader(reader){let changed=this.#reader?.token!==reader?.token;this.#reader=reader;try{this.#storage.setItem(readerStorageKey2(this.server,this.channel),reader?JSON.stringify(reader):"")}catch{}if(changed)this.#emit("reader",reader)}readerSignIn(options){return openReaderPopup2(this.server,this.channel,options).then((reader)=>(this.#setReader(reader),reader))}setReader(reader){this.#setReader(reader)}async readerSignOut(){let token=this.reader?.token;if(this.#setReader(null),token)await this.#call("/client/v1/reader-token",{method:"DELETE",token}).catch(()=>{return})}listDiscussions(page,options={}){return this.#discussionCall(`/client/v1/channels/${encodeURIComponent(this.channel)}/discussions${query({page,...options})}`,{},!1)}getDiscussion(id){return this.#discussionCall(`/client/v1/channels/${encodeURIComponent(this.channel)}/discussions/${encodeURIComponent(id)}`,{},!1)}startDiscussion(input){return this.#discussionCall("/client/v1/discussions",{method:"POST",json:input})}replyToDiscussion(discussionId,input){return this.#discussionCall(`/client/v1/discussions/${encodeURIComponent(discussionId)}/posts`,{method:"POST",json:input})}editDiscussionPost(postId,input){return this.#discussionCall(`/client/v1/discussion-posts/${encodeURIComponent(postId)}`,{method:"PATCH",json:input})}deleteDiscussionPost(postId){return this.#discussionCall(`/client/v1/discussion-posts/${encodeURIComponent(postId)}`,{method:"DELETE"})}voteDiscussionPost(postId,voted=!0){return this.#discussionCall(`/client/v1/discussion-posts/${encodeURIComponent(postId)}/vote`,{method:voted?"PUT":"DELETE"})}followDiscussion(discussionId,following=!0){return this.#discussionCall(`/client/v1/discussions/${encodeURIComponent(discussionId)}/follow`,{method:following?"PUT":"DELETE"})}acceptDiscussionAnswer(discussionId,postId){let path=`/client/v1/discussions/${encodeURIComponent(discussionId)}/answer`;return this.#discussionCall(path,postId?{method:"PUT",json:{post_id:postId}}:{method:"DELETE"})}async discussionWriter(){if(this.reader)return"reader";if(this.#session)return this.#session.contact.identified?"contact":null;return await this.#identity?.()?"contact":null}async#discussionCall(path,request,write=!0){let reader=this.reader;if(reader)try{return await this.#call(path,{...request,token:reader.token})}catch(error){if(!(error instanceof ApiError2)||error.status!==401||this.#reader?.token!==reader.token)throw error;if(this.#setReader(null),write)throw new ApiError2(403,"sign_in_required")}if(await this.discussionWriter()==="contact")return this.#authed(path,request);if(write)throw new ApiError2(403,"sign_in_required");return this.#call(path,request)}#idempotency(client_id){return this.#idempotencyKeys&&/^[\x20-\x7e]{1,255}$/.test(client_id)?{"Idempotency-Key":client_id}:{}}#messageRequest({body,subject,files=[],client_id=randomId2()}){let headers=this.#idempotency(client_id);if(files.length===0)return{method:"POST",headers,json:{body,subject,client_id}};let fields=[];if(subject)fields.push(["subject",subject]);if(body)fields.push(["body",body]);fields.push(["client_id",client_id]);let boundary=`yuva-${client_id.replace(/[^\w-]/g,"").slice(0,60)}-${client_id.length}`;return headers["Content-Type"]=`multipart/form-data; boundary=${boundary}`,{method:"POST",headers,body:multipart(fields,files,boundary)}}#call(path,request){return call(this.#fetch,this.server,path,request)}async#authed(path,request={},retry=!0){if(!this.#token)await this.start();try{return await this.#call(path,{...request,token:this.#token})}catch(error){if(!retry||!(error instanceof ApiError2)||error.status!==401)throw error;return await this.#renew(),this.#authed(path,request,!1)}}#renew(){return this.#renewing??=(async()=>{let session=await this.#open(!0);if(this.#realtime)this.#realtime.stop(),this.#realtime.reset(),this.#realtime.start();this.#emit("session",{session,renewed:!0})})().finally(()=>{this.#renewing=null}),this.#renewing}async#open(fresh){let stored=this.stored(),identity=await this.#identity?.()||null,subject=identity?jwtSubject(identity):null,saved=stored.session;if(!fresh&&saved&&saved.sub===subject&&Date.parse(saved.expires_at)>Date.now()+60000)try{let info=await this.#call("/client/v1/session",{token:saved.token});return this.#apply(info,saved.token,subject)}catch(error){if(!(error instanceof ApiError2)||error.status!==401&&error.status!==403)throw error}let session=await this.#call("/client/v1/session",{method:"POST",json:{channel_key:this.channel,identity_token:identity??void 0,visitor_id:stored.visitor_id}}),{token,...info}=session;return this.#apply(info,token,subject)}#apply(info,token,subject){this.#token=token,this.#session=info;let stored=this.stored();if(stored.session={token,expires_at:info.expires_at,sub:subject},info.visitor_id)stored.visitor_id=info.visitor_id;else if(info.contact.identified)delete stored.visitor_id;return this.#store(stored),info}}function createYuvaClient2(options){return new YuvaClient2(options)}
12
+ export{readerConnectUrl2,openReaderPopup2,storageKey2,readerStorageKey2,randomId2,YuvaClient2,createYuvaClient2};
@@ -0,0 +1,2 @@
1
+ class ApiError2 extends Error{status;code;constructor(status,code){super(`${status} ${code}`);this.status=status;this.code=code}}async function call(fetcher,server,path,{method="GET",json,body,headers={},token,blob}={}){if(headers={...headers},token)headers.Authorization=`Bearer ${token}`;let payload=body;if(json!==void 0)headers["Content-Type"]="application/json",payload=JSON.stringify(json);let response;try{response=await fetcher(server+path,{method,headers,body:payload,credentials:"omit"})}catch{throw new ApiError2(0,"network")}if(!response.ok){let code="error";try{code=(await response.json()).code??code}catch{}throw new ApiError2(response.status,code)}if(response.status===204)return;return blob?await response.blob():await response.json()}function mailingLists(fetcher,server,channel){return call(fetcher,server,`/client/v1/channels/${encodeURIComponent(channel)}/lists`)}function createSubscription(fetcher,server,channel,input){return call(fetcher,server,`/client/v1/channels/${encodeURIComponent(channel)}/subscriptions`,{method:"POST",json:input})}
2
+ export{ApiError2,call,mailingLists,createSubscription};
package/dist/esm/index.js CHANGED
@@ -1 +1 @@
1
- import{ApiError2,readerConnectUrl2,openReaderPopup2,YuvaClient2,createYuvaClient2}from"./index-dj5dhqc1.js";export{ApiError2 as ApiError,YuvaClient2 as YuvaClient,createYuvaClient2 as createYuvaClient,openReaderPopup2 as openReaderPopup,readerConnectUrl2 as readerConnectUrl};
1
+ import{readerConnectUrl2,openReaderPopup2,YuvaClient2,createYuvaClient2}from"./index-geeh5h34.js";import{ApiError2}from"./index-gk84nm8f.js";export{ApiError2 as ApiError,YuvaClient2 as YuvaClient,createYuvaClient2 as createYuvaClient,openReaderPopup2 as openReaderPopup,readerConnectUrl2 as readerConnectUrl};
@@ -1,5 +1,5 @@
1
- import { addComponent, defineNuxtModule } from "@nuxt/kit";
2
- const components = ["YuvaChat", "YuvaDiscussions", "YuvaPageFeedback"];
1
+ import { addComponent, addImports, addPlugin, defineNuxtModule } from "@nuxt/kit";
2
+ const components = ["YuvaChat", "YuvaDiscussions", "YuvaPageFeedback", "YuvaSubscribe"];
3
3
  export default defineNuxtModule({
4
4
  meta: { name: "useyuva", configKey: "yuva" },
5
5
  defaults: { server: "", channel: "", locale: "" },
@@ -8,7 +8,8 @@ export default defineNuxtModule({
8
8
  nuxt.options.runtimeConfig.public.yuva = {
9
9
  server: current.server || options.server || "",
10
10
  channel: current.channel || options.channel || "",
11
- locale: current.locale || options.locale || ""
11
+ locale: current.locale || options.locale || "",
12
+ analytics: current.analytics || options.analytics
12
13
  };
13
14
  const compilerOptions = nuxt.options.vue.compilerOptions ??= {};
14
15
  const isCustomElement = compilerOptions.isCustomElement;
@@ -16,5 +17,8 @@ export default defineNuxtModule({
16
17
  nuxt.options.build.transpile.push("useyuva");
17
18
  for (const name of components)
18
19
  addComponent({ name, export: name, filePath: "useyuva/nuxt/components" });
20
+ addImports({ name: "useYuvaAnalytics", from: "useyuva/nuxt/analytics" });
21
+ if (nuxt.options.runtimeConfig.public.yuva.analytics)
22
+ addPlugin({ src: "useyuva/nuxt/plugin", mode: "client" });
19
23
  }
20
24
  });
@@ -0,0 +1,6 @@
1
+ import { useNuxtApp } from "#imports";
2
+ const noop = () => {};
3
+ const idle = { pageview: noop, track: noop, notFound: noop, consent: noop, optOut: noop, optIn: noop };
4
+ export function useYuvaAnalytics() {
5
+ return useNuxtApp().$yuvaAnalytics ?? idle;
6
+ }
@@ -9,6 +9,15 @@ const shared = {
9
9
  };
10
10
  const docsProps = { ...shared, page: String, pageTitle: String };
11
11
  const chatProps = { ...shared, layout: String, open: Boolean };
12
+ const subscribeProps = {
13
+ ...shared,
14
+ lists: Array,
15
+ layout: String,
16
+ name: String,
17
+ buttonText: String,
18
+ consent: String,
19
+ privacyUrl: String
20
+ };
12
21
  function setup(tag, load, props, attributes, listeners) {
13
22
  const config = useRuntimeConfig().public.yuva;
14
23
  const element = ref(null);
@@ -86,3 +95,26 @@ export const YuvaChat = defineComponent({
86
95
  return render;
87
96
  }
88
97
  });
98
+ export const YuvaSubscribe = defineComponent({
99
+ name: "YuvaSubscribe",
100
+ props: subscribeProps,
101
+ emits: {
102
+ subscribe: (_) => true,
103
+ error: (_) => true
104
+ },
105
+ setup(props, { emit, expose }) {
106
+ const { element, render } = setup("yuva-subscribe", () => import("../../subscribe.js"), props, (p) => ({
107
+ lists: p.lists?.join(","),
108
+ layout: p.layout,
109
+ name: p.name,
110
+ "button-text": p.buttonText,
111
+ consent: p.consent,
112
+ "privacy-url": p.privacyUrl
113
+ }), {
114
+ onYuvaSubscribe: detail((d) => emit("subscribe", d)),
115
+ onYuvaSubscribeError: detail((d) => emit("error", d))
116
+ });
117
+ expose({ element });
118
+ return render;
119
+ }
120
+ });
@@ -0,0 +1,11 @@
1
+ import { defineNuxtPlugin, useRuntimeConfig } from "#imports";
2
+ import { createAnalytics } from "../../analytics.js";
3
+ export default defineNuxtPlugin(() => {
4
+ const config = useRuntimeConfig().public.yuva;
5
+ const options = config?.analytics;
6
+ if (!options?.key)
7
+ return;
8
+ return {
9
+ provide: { yuvaAnalytics: createAnalytics({ ...options, server: options.server || config?.server || "" }) }
10
+ };
11
+ });
package/dist/esm/react.js CHANGED
@@ -1 +1,2 @@
1
- import{createContext,createElement,useCallback,useContext,useEffect,useRef,useState}from"react";var YuvaContext=createContext(null);function YuvaProvider({client,connect=!0,children}){return useEffect(()=>{if(!connect)return;return client.connect().catch(()=>{return}),()=>client.disconnect()},[client,connect]),createElement(YuvaContext.Provider,{value:client},children)}function useYuva(){let client=useContext(YuvaContext);if(!client)throw Error("Yuva: wrap your components in <YuvaProvider client={...}>");return client}var time=(value)=>Date.parse(value),byCreated=(a,b)=>time(a.created_at)-time(b.created_at),byActivity=(a,b)=>time(b.last_message_at??b.created_at)-time(a.last_message_at??a.created_at),initial={items:[],loading:!0,error:null};function useConversations(){let client=useYuva(),[state,setState]=useState(initial),cursor=useRef(void 0),current=useRef(state);useEffect(()=>{current.current=state});let load=useCallback(async(more)=>{setState((s)=>({...s,loading:!0}));try{let page=await client.listConversations({limit:50,cursor:more?cursor.current:void 0});cursor.current=page.next_cursor,setState((s)=>{let items=new Map((more?s.items:[]).map((c)=>[c.id,c]));for(let conversation of page.items)items.set(conversation.id,conversation);return{items:[...items.values()].sort(byActivity),cursor:page.next_cursor,loading:!1,error:null}})}catch(error){setState((s)=>({...s,loading:!1,error}))}},[client]);return useEffect(()=>{load(!1);let offSession=client.on("session",({renewed})=>void(renewed&&load(!1))),offEvent=client.on("event",(message)=>{if(message.type==="resync_required")return void load(!1);if(message.type==="conversation.created")setState((s)=>s.items.some((c)=>c.id===message.data.id)?s:{...s,items:[message.data,...s.items].sort(byActivity)});else if(message.type==="conversation.updated")setState((s)=>({...s,items:s.items.map((c)=>c.id===message.conversation_id?{...c,status:message.data.status}:c)}));else if(message.type==="message.created"){let m=message.data;if(!current.current.items.some((c)=>c.id===m.conversation_id))return void load(!1);setState((s)=>{let items=s.items.map((c)=>c.id===m.conversation_id?{...c,last_message:{id:m.id,author_type:m.author.type,text:m.body.slice(0,140),created_at:m.created_at},last_message_at:m.created_at,unread:c.unread||m.author.type!=="contact"}:c);return{...s,items:items.sort(byActivity)}})}});return()=>{offSession(),offEvent()}},[client,load]),{conversations:state.items,loading:state.loading,error:state.error,hasMore:!!state.cursor,loadMore:()=>load(!0),reload:()=>load(!1)}}function useMessages(conversationId){let client=useYuva(),[state,setState]=useState(initial),cursor=useRef(void 0),upsert=useCallback((message)=>{setState((s)=>{let items=s.items.filter((m)=>m.id!==message.id&&(!message.client_id||m.client_id!==message.client_id));return{...s,items:[...items,message].sort(byCreated)}})},[]),load=useCallback(async(older)=>{if(!conversationId){cursor.current=void 0,setState({items:[],loading:!1,error:null});return}if(older&&!cursor.current)return;setState((s)=>({...s,loading:!0}));try{let page=await client.listMessages(conversationId,{order:"desc",limit:30,cursor:older?cursor.current:void 0});cursor.current=page.next_cursor,setState((s)=>{let items=new Map((older?s.items:[]).map((m)=>[m.id,m]));for(let message of page.items)items.set(message.id,message);return{items:[...items.values()].sort(byCreated),cursor:page.next_cursor,loading:!1,error:null}})}catch(error){setState((s)=>({...s,loading:!1,error}))}},[client,conversationId]);return useEffect(()=>{if(load(!1),!conversationId)return;let offSession=client.on("session",({renewed})=>void(renewed&&load(!1))),offEvent=client.on("event",(message)=>{if(message.type==="resync_required")load(!1);else if((message.type==="message.created"||message.type==="message.updated")&&message.data.conversation_id===conversationId)upsert(message.data)});return()=>{offSession(),offEvent()}},[client,conversationId,load,upsert]),{messages:state.items,loading:state.loading,error:state.error,hasMore:!!state.cursor,loadOlder:()=>load(!0),async send(input){if(!conversationId)throw Error("Yuva: no conversation; start one with client.startConversation()");let message=await client.sendMessage(conversationId,typeof input==="string"?{body:input}:input);return upsert(message),message},async markRead(){if(conversationId)await client.markRead(conversationId)}}}function useDocsElement(tag,props,handlers){let[element,setElement]=useState(null),latest=useRef(handlers);useEffect(()=>{latest.current=handlers});let{channel,server,locale,dir,page,pageTitle,identityToken,className,style}=props;useEffect(()=>{import("./docs.js")},[]),useEffect(()=>{if(!element)return;let attributes={server,locale,dir,page,"page-title":pageTitle,"identity-token":typeof identityToken==="string"?identityToken:void 0,channel};for(let[name,value]of Object.entries(attributes))if(value)element.setAttribute(name,value);else element.removeAttribute(name)},[element,channel,server,locale,dir,page,pageTitle,identityToken]),useEffect(()=>{if(!element||typeof identityToken!=="function")return;let current=!0;return customElements.whenDefined(tag).then(()=>{if(current)element.setIdentityToken(identityToken)}),()=>{current=!1}},[element,tag,identityToken]);let events=Object.keys(handlers).join(" ");return useEffect(()=>{if(!element)return;let listeners=events.split(" ").map((type)=>{let listener=(event)=>latest.current[type]?.(event.detail);return element.addEventListener(type,listener),()=>element.removeEventListener(type,listener)});return()=>listeners.forEach((off)=>off())},[element,events]),createElement(tag,{ref:setElement,className,style})}function YuvaPageFeedback({onRating,onFeedback,...props}){return useDocsElement("yuva-page-feedback",props,{"yuva-rating":onRating,"yuva-feedback":onFeedback})}function YuvaDiscussions({onDiscussion,onPost,onReader,...props}){return useDocsElement("yuva-discussions",props,{"yuva-discussion":onDiscussion,"yuva-post":onPost,"yuva-reader":onReader})}export{YuvaDiscussions,YuvaPageFeedback,YuvaProvider,useConversations,useMessages,useYuva};
1
+ "use client";
2
+ import{createContext,createElement,useCallback,useContext,useEffect,useRef,useState}from"react";var current,pending=[],call=(name)=>(...args)=>current?current[name](...args):void pending.push([name,args]),analytics={pageview:call("pageview"),track:call("track"),notFound:call("notFound"),consent:call("consent"),optOut:call("optOut"),optIn:call("optIn")};function attachAnalytics(instance){if(current=instance,instance)for(let[name,args]of pending.splice(0))instance[name](...args)}var YuvaContext=createContext(null);function YuvaProvider({client,connect=!0,children}){return useEffect(()=>{if(!connect)return;return client.connect().catch(()=>{return}),()=>client.disconnect()},[client,connect]),createElement(YuvaContext.Provider,{value:client},children)}function useYuva(){let client=useContext(YuvaContext);if(!client)throw Error("Yuva: wrap your components in <YuvaProvider client={...}>");return client}var time=(value)=>Date.parse(value),byCreated=(a,b)=>time(a.created_at)-time(b.created_at),byActivity=(a,b)=>time(b.last_message_at??b.created_at)-time(a.last_message_at??a.created_at),initial={items:[],loading:!0,error:null};function useConversations(){let client=useYuva(),[state,setState]=useState(initial),cursor=useRef(void 0),current=useRef(state);useEffect(()=>{current.current=state});let load=useCallback(async(more)=>{setState((s)=>({...s,loading:!0}));try{let page=await client.listConversations({limit:50,cursor:more?cursor.current:void 0});cursor.current=page.next_cursor,setState((s)=>{let items=new Map((more?s.items:[]).map((c)=>[c.id,c]));for(let conversation of page.items)items.set(conversation.id,conversation);return{items:[...items.values()].sort(byActivity),cursor:page.next_cursor,loading:!1,error:null}})}catch(error){setState((s)=>({...s,loading:!1,error}))}},[client]);return useEffect(()=>{load(!1);let offSession=client.on("session",({renewed})=>void(renewed&&load(!1))),offEvent=client.on("event",(message)=>{if(message.type==="resync_required")return void load(!1);if(message.type==="conversation.created")setState((s)=>s.items.some((c)=>c.id===message.data.id)?s:{...s,items:[message.data,...s.items].sort(byActivity)});else if(message.type==="conversation.updated")setState((s)=>({...s,items:s.items.map((c)=>c.id===message.conversation_id?{...c,status:message.data.status}:c)}));else if(message.type==="message.created"){let m=message.data;if(!current.current.items.some((c)=>c.id===m.conversation_id))return void load(!1);setState((s)=>{let items=s.items.map((c)=>c.id===m.conversation_id?{...c,last_message:{id:m.id,author_type:m.author.type,text:m.body.slice(0,140),created_at:m.created_at},last_message_at:m.created_at,unread:c.unread||m.author.type!=="contact"}:c);return{...s,items:items.sort(byActivity)}})}});return()=>{offSession(),offEvent()}},[client,load]),{conversations:state.items,loading:state.loading,error:state.error,hasMore:!!state.cursor,loadMore:()=>load(!0),reload:()=>load(!1)}}function useMessages(conversationId){let client=useYuva(),[state,setState]=useState(initial),cursor=useRef(void 0),upsert=useCallback((message)=>{setState((s)=>{let items=s.items.filter((m)=>m.id!==message.id&&(!message.client_id||m.client_id!==message.client_id));return{...s,items:[...items,message].sort(byCreated)}})},[]),load=useCallback(async(older)=>{if(!conversationId){cursor.current=void 0,setState({items:[],loading:!1,error:null});return}if(older&&!cursor.current)return;setState((s)=>({...s,loading:!0}));try{let page=await client.listMessages(conversationId,{order:"desc",limit:30,cursor:older?cursor.current:void 0});cursor.current=page.next_cursor,setState((s)=>{let items=new Map((older?s.items:[]).map((m)=>[m.id,m]));for(let message of page.items)items.set(message.id,message);return{items:[...items.values()].sort(byCreated),cursor:page.next_cursor,loading:!1,error:null}})}catch(error){setState((s)=>({...s,loading:!1,error}))}},[client,conversationId]);return useEffect(()=>{if(load(!1),!conversationId)return;let offSession=client.on("session",({renewed})=>void(renewed&&load(!1))),offEvent=client.on("event",(message)=>{if(message.type==="resync_required")load(!1);else if((message.type==="message.created"||message.type==="message.updated")&&message.data.conversation_id===conversationId)upsert(message.data)});return()=>{offSession(),offEvent()}},[client,conversationId,load,upsert]),{messages:state.items,loading:state.loading,error:state.error,hasMore:!!state.cursor,loadOlder:()=>load(!0),async send(input){if(!conversationId)throw Error("Yuva: no conversation; start one with client.startConversation()");let message=await client.sendMessage(conversationId,typeof input==="string"?{body:input}:input);return upsert(message),message},async markRead(){if(conversationId)await client.markRead(conversationId)}}}function useYuvaElement(tag,load,props,extra,handlers){let[element,setElement]=useState(null),latest=useRef(handlers),loader=useRef(load);useEffect(()=>{latest.current=handlers});let{channel,server,locale,dir,identityToken,className,style}=props;useEffect(()=>{loader.current()},[]);let attributes=JSON.stringify({server,locale,dir,...extra,"identity-token":typeof identityToken==="string"?identityToken:void 0,channel});useEffect(()=>{if(!element)return;for(let[name,value]of Object.entries(JSON.parse(attributes)))if(value)element.setAttribute(name,value);else element.removeAttribute(name)},[element,attributes]),useEffect(()=>{if(!element||typeof identityToken!=="function")return;let current=!0;return customElements.whenDefined(tag).then(()=>{if(current)element.setIdentityToken(identityToken)}),()=>{current=!1}},[element,tag,identityToken]);let events=Object.keys(handlers).join(" ");return useEffect(()=>{if(!element)return;let listeners=events.split(" ").map((type)=>{let listener=(event)=>latest.current[type]?.(event.detail);return element.addEventListener(type,listener),()=>element.removeEventListener(type,listener)});return()=>listeners.forEach((off)=>off())},[element,events]),createElement(tag,{ref:setElement,className,style})}var loadDocs=()=>import("./docs.js");function YuvaPageFeedback({onRating,onFeedback,...props}){return useYuvaElement("yuva-page-feedback",loadDocs,props,{page:props.page,"page-title":props.pageTitle},{"yuva-rating":onRating,"yuva-feedback":onFeedback})}function YuvaDiscussions({onDiscussion,onPost,onReader,...props}){return useYuvaElement("yuva-discussions",loadDocs,props,{page:props.page,"page-title":props.pageTitle},{"yuva-discussion":onDiscussion,"yuva-post":onPost,"yuva-reader":onReader})}function YuvaSubscribe({onSubscribe,onError,...props}){return useYuvaElement("yuva-subscribe",()=>import("./subscribe.js"),props,{lists:props.lists?.join(","),layout:props.layout,name:props.name,"button-text":props.buttonText,consent:props.consent,"privacy-url":props.privacyUrl},{"yuva-subscribe":onSubscribe,"yuva-subscribe-error":onError})}function YuvaAnalytics({server,publicKey:key,hash,consent,manual,outbound,files,chat}){return useEffect(()=>{let instance,mounted=!0;return import("./analytics.js").then(({createAnalytics})=>{if(!mounted)return;instance=createAnalytics({server,key,hash,consent,manual,outbound,files,chat}),attachAnalytics(instance)}),()=>{mounted=!1,instance?.destroy(),attachAnalytics(void 0)}},[server,key,hash,consent,manual,outbound,files,chat]),null}function useYuvaAnalytics(){return analytics}export{YuvaAnalytics,YuvaDiscussions,YuvaPageFeedback,YuvaProvider,YuvaSubscribe,useConversations,useMessages,useYuva,useYuvaAnalytics};
@@ -0,0 +1,33 @@
1
+ import{el}from"./index-rdzxvj2r.js";import{ApiError2,call,mailingLists,createSubscription}from"./index-gk84nm8f.js";import{translate,localeIn,directionOf,contrastOn}from"./index-98r96a7e.js";var messages=JSON.parse('{"-g5O6h":["Choose at least one list."],"1w51t9":["Subscribing…"],"EDl9kS":["Subscribe"],"E_LjOR":["Enter your name."],"E_qvzP":["Enter a valid e-mail address."],"GQVIIl":["You seem to be offline."],"ScJ9fj":["Privacy policy"],"XJURKP":["E-mail address"],"YnOaHp":["Your name (optional)"],"dMJfG5":["I want to receive e-mails about ",["list"]," from ",["inbox"],". I can unsubscribe at any time."],"eku1-t":["Your account has no e-mail address yet."],"h16FyT":["Lists"],"jWgNnM":["Could not subscribe. Try again."],"lDEbTy":["Tick the box to agree."],"q8yluz":["Your name"],"rgySXy":["Thanks! You are subscribed."],"tzsBOP":["Too many attempts. Wait a moment and try again."],"x6dU53":["Check your inbox to confirm."],"y3k2zM":["Almost done. Check your inbox and confirm your subscription."]}');var messages2=JSON.parse('{"-g5O6h":["Wählen Sie mindestens eine Liste."],"1w51t9":["Wird angemeldet…"],"EDl9kS":["Abonnieren"],"E_LjOR":["Geben Sie Ihren Namen ein."],"E_qvzP":["Geben Sie eine gültige E-Mail-Adresse ein."],"GQVIIl":["Sie scheinen offline zu sein."],"ScJ9fj":["Datenschutzerklärung"],"XJURKP":["E-Mail-Adresse"],"YnOaHp":["Ihr Name (optional)"],"dMJfG5":["Ich möchte E-Mails von ",["inbox"]," zu ",["list"]," erhalten. Ich kann mich jederzeit abmelden."],"eku1-t":["Ihr Konto hat noch keine E-Mail-Adresse."],"h16FyT":["Listen"],"jWgNnM":["Anmeldung fehlgeschlagen. Versuchen Sie es erneut."],"lDEbTy":["Setzen Sie das Häkchen, um zuzustimmen."],"q8yluz":["Ihr Name"],"rgySXy":["Danke! Sie sind angemeldet."],"tzsBOP":["Zu viele Versuche. Warten Sie einen Moment und versuchen Sie es erneut."],"x6dU53":["Sehen Sie zur Bestätigung in Ihr Postfach."],"y3k2zM":["Fast geschafft. Sehen Sie in Ihr Postfach und bestätigen Sie Ihr Abonnement."]}');var messages3=JSON.parse('{"-g5O6h":["En az bir liste seçin."],"1w51t9":["Abone olunuyor…"],"EDl9kS":["Abone ol"],"E_LjOR":["Adınızı girin."],"E_qvzP":["Geçerli bir e-posta adresi girin."],"GQVIIl":["Çevrimdışı görünüyorsunuz."],"ScJ9fj":["Gizlilik politikası"],"XJURKP":["E-posta adresi"],"YnOaHp":["Adınız (isteğe bağlı)"],"dMJfG5":[["list"]," hakkında ",["inbox"]," tarafından gönderilen e-postaları almak istiyorum. Aboneliğimi istediğim zaman bırakabilirim."],"eku1-t":["Hesabınızda henüz bir e-posta adresi yok."],"h16FyT":["Listeler"],"jWgNnM":["Abone olunamadı. Yeniden deneyin."],"lDEbTy":["Onaylamak için kutuyu işaretleyin."],"q8yluz":["Adınız"],"rgySXy":["Teşekkürler! Abone oldunuz."],"tzsBOP":["Çok fazla deneme. Biraz bekleyip tekrar deneyin."],"x6dU53":["Onaylamak için gelen kutunuza bakın."],"y3k2zM":["Neredeyse bitti. Gelen kutunuza bakın ve aboneliğinizi onaylayın."]}');var catalogs={en:messages,tr:messages3,de:messages2},tokenMinimumAge=2100,fetcher=(url,init)=>fetch(url,init),Base=typeof HTMLElement>"u"?class{}:HTMLElement,scriptSrc=typeof document<"u"&&document.currentScript instanceof HTMLScriptElement?document.currentScript.src:"",styles=`
2
+ :host{display:block;line-height:1.5}
3
+ :host([hidden]),[hidden]{display:none!important}
4
+ *{box-sizing:border-box}
5
+ p,form,fieldset{margin:0}
6
+ .frame,form,.row,fieldset,.text{display:flex;flex-direction:column}
7
+ .frame{--a:var(--yuva-accent,var(--brand,#2563eb));--on:var(--yuva-on-accent,var(--brand-on,#fff));--r:var(--yuva-radius,8px);--d:#dc2626;font-family:var(--yuva-font,inherit);gap:10px}
8
+ @media (prefers-color-scheme:dark){.frame{--d:#f87171}}
9
+ form{gap:10px}
10
+ .row,fieldset{gap:8px}
11
+ .inline .row{flex-flow:row wrap}
12
+ .inline .row input{flex:1 1 180px}
13
+ input{inline-size:100%;padding:8px 10px;border:1px solid rgb(127 127 127/.35);border-radius:var(--r);background:none;color:inherit;font:inherit}
14
+ [aria-invalid=true]{border-color:var(--d)}
15
+ [type=checkbox]{inline-size:18px;block-size:18px;margin:2px 0 0;flex:none;accent-color:var(--a)}
16
+ [role=switch]{appearance:none;position:relative;inline-size:36px;block-size:20px;margin:1px 0 0;border-radius:10px;background:rgb(127 127 127/.45);cursor:pointer;transition:background .15s}
17
+ [role=switch]::before{content:"";position:absolute;inset-block-start:2px;inset-inline-start:2px;inline-size:16px;block-size:16px;border-radius:50%;background:#fff;box-shadow:0 1px 2px rgb(0 0 0/.3);transition:inset-inline-start .15s}
18
+ [role=switch]:checked{background:var(--a)}
19
+ [role=switch]:checked::before{inset-inline-start:18px;background:var(--on)}
20
+ [role=switch]:disabled{opacity:.5;cursor:default}
21
+ button{padding:8px 16px;border:0;border-radius:var(--r);background:var(--a);color:var(--on);font:inherit;font-weight:600;cursor:pointer}
22
+ button:disabled{opacity:.6;cursor:default}
23
+ :focus-visible{outline:2px solid var(--a);outline-offset:2px}
24
+ fieldset{padding:0;border:0}
25
+ legend{padding:0;margin-block-end:4px;font-weight:600}
26
+ .check{display:flex;gap:8px;align-items:flex-start;cursor:pointer}
27
+ .muted,.consent,.error{font-size:.9em}
28
+ .muted{opacity:.72}
29
+ .consent a{color:inherit}
30
+ .error{color:var(--d)}
31
+ .trap{position:absolute;inset-inline-start:-1e4px;inline-size:1px;overflow:hidden}
32
+ @media (max-width:480px){input{font-size:16px}}
33
+ `;class YuvaSubscribeElement extends Base{static observedAttributes=["channel","server","locale","dir","lists","layout","name","button-text","consent","privacy-url","identity-token"];static loadClient=null;#root;#messages=messages;#connected=!1;#generation=0;#phase="loading";#refused=!1;#inbox=null;#offered=[];#chosen=new Set;#token="";#tokenAt=0;#confirmation=!1;#error="";#emailError="";#nameError="";#consentError="";#identity=null;#client=null;#busy=new Set;#email=el("input");#name=el("input");#trap=el("input");#consent=el("input");#status=el("p");constructor(){super();this.#root=this.attachShadow({mode:"open"}),this.#email.type="email",this.#email.autocomplete="email",this.#email.required=!0,this.#email.maxLength=320,this.#email.id="email",this.#name.autocomplete="name",this.#name.maxLength=200,this.#name.id="name",this.#trap.name="website",this.#trap.tabIndex=-1,this.#trap.autocomplete="off",this.#consent.type="checkbox",this.#consent.id="consent",this.#consent.addEventListener("change",()=>{this.#consentError="",this.#render()});for(let input of[this.#email,this.#name])input.setAttribute("part","field"),input.dir="auto";this.#status.setAttribute("part","status"),this.#status.setAttribute("role","status"),this.#status.setAttribute("aria-live","polite")}get channel(){return this.getAttribute("channel")}set channel(value){this.#attribute("channel",value)}get server(){return(this.getAttribute("server")||(scriptSrc?new URL(scriptSrc).origin:location.origin)).replace(/\/+$/,"")}set server(value){this.#attribute("server",value)}get locale(){return localeIn(catalogs,[this.getAttribute("locale"),navigator.language])}set locale(value){this.#attribute("locale",value)}get lists(){return this.#offered.map((item)=>item.list)}setIdentityToken(source){if(this.#identity=source,this.#connected)this.#load()}connectedCallback(){this.#connected=!0,this.#load()}disconnectedCallback(){this.#connected=!1}attributeChangedCallback(name,previous,value){if(!this.#connected||previous===value)return;if(["channel","server","lists","identity-token"].includes(name))return void this.#load();this.#render()}#attribute(name,value){if(value===null||value===void 0||value==="")this.removeAttribute(name);else this.setAttribute(name,value)}#t(descriptor){return translate(this.#messages,descriptor,messages)}get#identified(){return!!this.#identity||this.hasAttribute("identity-token")}async#identityToken(){return this.#identity?await this.#identity()??null:this.getAttribute("identity-token")}#wanted(lists){let ids=(this.getAttribute("lists")??"").split(",").map((id)=>id.trim()).filter((id)=>id&&id!=="all");if(ids.length===0)return lists;let found=ids.map((id)=>lists.find((list)=>list.id===id)).filter((list)=>!!list);if(found.length<ids.length)console.warn(`Yuva: some lists in "${ids.join(",")}" are not public lists`);return found}async#load(){let generation=++this.#generation,channel=this.channel;if(this.#refused=!1,this.#phase="loading",this.#offered=[],this.#error="",this.#client=null,this.#render(),!channel)return;let server=this.server;try{let inbox=call(fetcher,server,`/client/v1/channels/${encodeURIComponent(channel)}`),offered=null;if(this.#identified&&YuvaSubscribeElement.loadClient&&await this.#identityToken()){let{createYuvaClient}=await YuvaSubscribeElement.loadClient(),client=createYuvaClient({server,channel,identityToken:()=>this.#identityToken()});try{let{items}=await client.subscriptions(),wanted=new Set(this.#wanted(items.map((item)=>item.list)));offered=items.filter((item)=>wanted.has(item.list)),this.#client=client}catch(error){if(!(error instanceof ApiError2)||error.code!=="identity_required")throw error}}if(!offered){let{lists,form_token}=await mailingLists(fetcher,server,channel);offered=this.#wanted(lists).map((list)=>({list})),this.#token=form_token,this.#tokenAt=Date.now()}if(this.#inbox=await inbox,generation!==this.#generation)return;if(this.#offered=offered,this.#chosen=new Set(offered.length===1?[offered[0].list.id]:[]),this.#phase="idle",offered.length===0)console.warn(`Yuva: channel ${channel} has no public mailing lists to offer`)}catch(error){if(generation!==this.#generation)return;if(this.#refusal(error))return;this.#phase="idle",this.#error=this.#errorText(error)}this.#render()}#refusal(error){let refused=error instanceof ApiError2&&(error.status===404||error.code==="origin_not_allowed"||error.code==="page_origin");if(refused)this.#refused=!0,console.warn(`Yuva: channel ${this.channel} is unknown, offers no such list or does not allow ${location.origin}`),this.#render();return refused}#errorText(error){let code=error instanceof ApiError2?error.code:"";if(code==="rate_limited")return this.#t({id:"tzsBOP"});if(code==="network")return this.#t({id:"GQVIIl"});if(code==="no_email")return this.#t({id:"eku1-t"});return this.#t({id:"jWgNnM"})}#emit(type,detail){this.dispatchEvent(new CustomEvent(type,{detail,bubbles:!0,composed:!0}))}#failed(error){this.#emit("yuva-subscribe-error",{code:error instanceof ApiError2?error.code:"error"})}consentText(lists=this.#selected()){let custom=this.getAttribute("consent")?.trim();if(custom)return custom;let names=(lists.length?lists:this.lists).map((list)=>list.name),list=new Intl.ListFormat(this.locale,{type:"conjunction"}).format(names),inbox=this.#inbox?.name??"";return this.#t({id:"dMJfG5",values:{list,inbox}})}#selected(){return this.lists.filter((list)=>this.#chosen.has(list.id))}async#submit(){if(this.#phase!=="idle"||!this.channel)return;let lists=this.#selected(),email=this.#email.value.trim(),name=this.#name.value.trim(),asksName=this.getAttribute("name");if(this.#error=lists.length===0?this.#t({id:"-g5O6h"}):"",this.#emailError=this.#email.checkValidity()&&email?"":this.#t({id:"E_qvzP"}),this.#nameError=asksName==="required"&&!name?this.#t({id:"E_LjOR"}):"",this.#consentError=this.#consent.checked?"":this.#t({id:"lDEbTy"}),this.#error||this.#emailError||this.#nameError||this.#consentError){this.#render();let invalid=[this.#emailError&&this.#email,this.#nameError&&this.#name,this.#consentError&&this.#consent];for(let input of invalid)if(input)return input.focus();return}let channel=this.channel,generation=this.#generation,consent_text=this.consentText(lists);this.#phase="submitting",this.#render();let send=async(retry)=>{let wait=this.#tokenAt+tokenMinimumAge-Date.now();if(wait>0)await new Promise((resolve)=>setTimeout(resolve,wait));try{let{confirmation}=await createSubscription(fetcher,this.server,channel,{email,name:asksName==="optional"||asksName==="required"?name||void 0:void 0,list_ids:lists.map((list)=>list.id),locale:this.locale,page_url:location.href.split("#")[0].slice(0,2000),consent_text,form_token:this.#token,website:this.#trap.value||void 0});return this.#confirmation=confirmation,!0}catch(error){if(retry&&error instanceof ApiError2&&error.code==="form_token"){let fresh=await mailingLists(fetcher,this.server,channel);return this.#token=fresh.form_token,this.#tokenAt=Date.now(),send(!1)}throw error}};try{await send(!0)}catch(error){if(generation!==this.#generation)return;if(this.#failed(error),this.#phase="idle",this.#refusal(error))return;if(error instanceof ApiError2&&error.code==="invalid_email")return this.#emailError=this.#t({id:"E_qvzP"}),this.#render(),this.#email.focus();return this.#error=this.#errorText(error),this.#render()}if(generation!==this.#generation)return;this.#phase="done",this.#emit("yuva-subscribe",{lists:lists.map((list)=>list.id),confirmation:this.#confirmation}),this.#render()}async#toggle(item,on){let client=this.#client;if(!client||this.#busy.has(item.list.id))return;this.#busy.add(item.list.id),this.#error="",this.#render();try{if(on){let{state}=await client.subscribeList(item.list.id,{consent_text:this.consentText([item.list]),page_url:location.href.split("#")[0].slice(0,2000)});item.state=state,this.#emit("yuva-subscribe",{lists:[item.list.id],confirmation:state==="pending"})}else await client.unsubscribeList(item.list.id),item.state="unsubscribed"}catch(error){this.#failed(error),this.#error=this.#errorText(error)}if(this.#busy.delete(item.list.id),client===this.#client)this.#render()}#errorFor(input,text){if(input.setAttribute("aria-invalid",String(!!text)),!text)return input.removeAttribute("aria-describedby"),null;let id=`${input.id}-error`;input.setAttribute("aria-describedby",id);let error=el("p","error",text);return error.id=id,error}#check(text,checked,onChange,key){let label=el("label","check"),box=el("input");box.type="checkbox",box.checked=checked,box.dataset.key=key,box.addEventListener("change",()=>onChange(box.checked));let body=el("span","text");return body.append(...text),label.append(box,body),label}#lists(identity){if(!identity&&this.#offered.length<2)return null;let group=el("fieldset");group.append(el("legend","",this.#t({id:"h16FyT"})));for(let item of this.#offered){let text=[el("span","",item.list.name)];if(item.list.description)text.push(el("span","muted",item.list.description));if(identity){if(text.push(el("span","muted consent",this.consentText([item.list]))),item.state==="pending")text.push(el("span","muted",this.#t({id:"x6dU53"})))}let on=identity?item.state==="subscribed"||item.state==="pending":this.#chosen.has(item.list.id),label=this.#check(text,on,(checked)=>{if(identity)return void this.#toggle(item,checked);if(checked)this.#chosen.add(item.list.id);else this.#chosen.delete(item.list.id);this.#error="",this.#render()},item.list.id),box=label.querySelector("input");if(identity)box.setAttribute("role","switch"),box.setAttribute("aria-checked",String(box.checked)),box.addEventListener("keydown",(event)=>{if(event.key!=="Enter")return;event.preventDefault(),box.click()}),box.disabled=this.#busy.has(item.list.id);else box.disabled=this.#phase==="submitting";group.append(label)}return group}#form(){let form=el("form",this.getAttribute("layout")==="inline"?"inline":"");form.noValidate=!0,form.addEventListener("submit",(event)=>{event.preventDefault(),this.#submit()});let busy=this.#phase==="submitting",lists=this.#lists(!1);if(lists)form.append(lists);let row=el("div","row"),errors=[],asksName=this.getAttribute("name");if(asksName==="optional"||asksName==="required"){this.#name.placeholder=asksName==="required"?this.#t({id:"q8yluz"}):this.#t({id:"YnOaHp"}),this.#name.setAttribute("aria-label",this.#name.placeholder),this.#name.required=asksName==="required",this.#name.disabled=busy,row.append(this.#name);let error=this.#errorFor(this.#name,this.#nameError);if(error)errors.push(error)}this.#email.placeholder=this.#t({id:"XJURKP"}),this.#email.setAttribute("aria-label",this.#email.placeholder),this.#email.disabled=busy,row.append(this.#email);let emailError=this.#errorFor(this.#email,this.#emailError);if(emailError)errors.push(emailError);let button=el("button","",busy?this.#t({id:"1w51t9"}):this.getAttribute("button-text")||this.#t({id:"EDl9kS"}));button.type="submit",button.disabled=busy,button.setAttribute("part","button"),row.append(button);let trap=el("div","trap");trap.setAttribute("aria-hidden","true"),trap.append(this.#trap);let consentText=el("span","",this.consentText()),consent=el("label","check consent");consent.setAttribute("part","consent"),this.#consent.disabled=busy;let body=el("span");body.append(consentText);let privacy=this.getAttribute("privacy-url");if(privacy&&/^https?:\/\//i.test(privacy)){let link=el("a","",this.#t({id:"ScJ9fj"}));link.href=privacy,link.target="_blank",link.rel="noopener noreferrer",body.append(" ",link)}consent.append(this.#consent,body);let consentError=this.#errorFor(this.#consent,this.#consentError);if(form.append(row,...errors,consent),consentError)form.append(consentError);return form.append(trap),form}#render(){if(this.#refused||!this.channel||this.#phase!=="loading"&&this.#offered.length===0&&!this.#error){this.#root.replaceChildren();return}let locale=this.locale;this.#messages=catalogs[locale]??messages;let dir=this.getAttribute("dir"),wrapper=el("div","frame");wrapper.dir=dir==="rtl"||dir==="ltr"?dir:directionOf(locale),wrapper.lang=locale;let brand=this.#inbox?.chat.launcher_color??this.#inbox?.branding.color;if(brand&&/^#[0-9a-f]{6}$/i.test(brand))wrapper.style.setProperty("--brand",brand),wrapper.style.setProperty("--brand-on",contrastOn(brand));let status="";if(this.#phase==="done")status=this.#confirmation?this.#t({id:"y3k2zM"}):this.#t({id:"rgySXy"});if(this.#status.textContent=status,this.#phase==="idle"||this.#phase==="submitting"){let content=this.#client?this.#lists(!0):this.#form();if(content)wrapper.append(content)}if(wrapper.append(this.#status),this.#error){let error=el("p","error",this.#error);error.setAttribute("role","alert"),wrapper.append(error)}let focusedKey,focused=null;try{focused=this.#root.activeElement}catch{}if(focused instanceof HTMLInputElement)focusedKey=focused.dataset.key;let style=el("style");style.textContent=styles,this.#root.replaceChildren(style,wrapper);let again=focusedKey?this.#root.querySelector(`[data-key="${focusedKey}"]`):focused;if(again instanceof HTMLElement&&again.isConnected)again.focus({preventScroll:!0})}}function defineSubscribe(){if(typeof customElements>"u")return;if(!customElements.get("yuva-subscribe"))customElements.define("yuva-subscribe",YuvaSubscribeElement)}YuvaSubscribeElement.loadClient=()=>import("./client-9wy8zy77.js");defineSubscribe();export{YuvaSubscribeElement,defineSubscribe};