useyuva 0.0.12 → 0.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +244 -2
- package/dist/esm/chat.js +2 -2
- package/dist/esm/docs.js +4 -4
- package/dist/esm/h3.js +1 -0
- package/dist/esm/{index-c6q8br7d.js → index-9vqd1b2a.js} +1 -1
- package/dist/esm/index-qcd538se.js +2 -0
- package/dist/esm/index-r9f9ttb2.js +2 -0
- package/dist/esm/nitro/module.js +39 -0
- package/dist/esm/nitro/runtime/core.js +64 -0
- package/dist/esm/nitro/runtime/define.js +3 -0
- package/dist/esm/nitro/runtime/identity.js +3 -0
- package/dist/esm/nitro/runtime/plugin.js +3 -0
- package/dist/esm/nitro/runtime/webhook.js +3 -0
- package/dist/esm/nitro/setup.js +65 -0
- package/dist/esm/nuxt/module.js +64 -2
- package/dist/esm/nuxt/runtime/components.js +8 -1
- package/dist/esm/server.js +1 -0
- package/dist/esm/subscribe.js +2 -2
- package/dist/esm/{thread-btm71nxt.js → thread-gx5pssds.js} +1 -1
- package/dist/esm/{yuva-chat-kf9vz8r3.js → yuva-chat-025wzr6w.js} +2 -2
- package/dist/types/h3.d.ts +36 -0
- package/dist/types/nitro/module.d.ts +8 -0
- package/dist/types/nitro/runtime/core.d.ts +41 -0
- package/dist/types/nitro/runtime/define.d.ts +21 -0
- package/dist/types/nitro/setup.d.ts +36 -0
- package/dist/types/nuxt/module.d.ts +17 -3
- package/dist/types/server/analytics.d.ts +52 -0
- package/dist/types/server/encoding.d.ts +6 -0
- package/dist/types/server/identity.d.ts +14 -0
- package/dist/types/server/webhook.d.ts +28 -0
- package/dist/types/server.d.ts +3 -0
- package/dist/types/styles.d.ts +2 -0
- package/dist/yuva-chat.js +6 -6
- package/dist/yuva-docs.js +6 -6
- package/dist/yuva-subscribe.js +2 -2
- package/dist/yuva.js +2 -2
- package/package.json +26 -2
- package/dist/esm/index-98r96a7e.js +0 -2
package/README.md
CHANGED
|
@@ -10,8 +10,10 @@ JavaScript SDK for [Yuva](https://github.com/productdevbook/yuva), open-source c
|
|
|
10
10
|
| `useyuva/subscribe` | `<yuva-subscribe>`, a mailing list sign-up form. Registers in the browser only. |
|
|
11
11
|
| `useyuva/analytics` | `createAnalytics()`: cookieless pageviews and engagement for an analytics property. Does nothing during SSR. |
|
|
12
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
|
|
13
|
+
| `useyuva/nuxt` | A Nuxt module: `YuvaChat`, `YuvaPageFeedback`, `YuvaDiscussions` and `YuvaSubscribe` components, configured once in `nuxt.config`, and the identity and webhook routes on the server. |
|
|
14
|
+
| `useyuva/nitro` | A Nitro v3 module serving `/_yuva/identity` and `/_yuva/webhook` and sending server-side analytics; `defineYuvaServer` from `useyuva/nitro/runtime`. |
|
|
14
15
|
| `useyuva/api` | A typed `/v1` client for backends (API key), generated from the OpenAPI contract. |
|
|
16
|
+
| `useyuva/server` | `signIdentityToken`, `verifyWebhook` and `createAnalytics` for backends. Web Crypto and `fetch` only: Node 22+, Bun, Deno, workers, srvx. |
|
|
15
17
|
|
|
16
18
|
```sh
|
|
17
19
|
npm install useyuva
|
|
@@ -273,7 +275,46 @@ export default defineNuxtConfig({
|
|
|
273
275
|
`NUXT_PUBLIC_YUVA_SERVER`, `NUXT_PUBLIC_YUVA_CHANNEL`, `NUXT_PUBLIC_YUVA_LOCALE` and, when
|
|
274
276
|
`analytics` is set, `NUXT_PUBLIC_YUVA_ANALYTICS_KEY` override the options at runtime. `analytics`
|
|
275
277
|
takes `key`, `server` (defaults to `yuva.server`), `hash`, `consent`, `manual`, `outbound`, `files` and `chat`, adds a client
|
|
276
|
-
plugin and auto-imports `useYuvaAnalytics()`.
|
|
278
|
+
plugin and auto-imports `useYuvaAnalytics()`.
|
|
279
|
+
|
|
280
|
+
For signed-in users, the only code you write is who they are:
|
|
281
|
+
|
|
282
|
+
```ts
|
|
283
|
+
// server/yuva.ts
|
|
284
|
+
export default defineYuvaServer({
|
|
285
|
+
user: async (event) => {
|
|
286
|
+
const { user } = await getUserSession(event); // your session; null when signed out
|
|
287
|
+
return user && { subject: user.id, email: user.email, emailVerified: user.emailConfirmed, name: user.name };
|
|
288
|
+
},
|
|
289
|
+
webhooks: {
|
|
290
|
+
"message.created": async (payload) => {
|
|
291
|
+
if (!payload.data.contact?.online) await sendPush(payload.data);
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
});
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
```sh
|
|
298
|
+
NUXT_YUVA_IDENTITY_SECRET=yuva_is_… # at build and at runtime
|
|
299
|
+
NUXT_YUVA_WEBHOOK_SECRET=whsec_…
|
|
300
|
+
NUXT_YUVA_ANALYTICS_API_KEY=yuva_…
|
|
301
|
+
NUXT_YUVA_ANALYTICS_PROPERTY_ID=…
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
With an identity secret, the module serves `GET /_yuva/identity` (the [h3](#h3) identity handler for
|
|
305
|
+
`user(event)`) and every component fetches its token there on its own; pass `identityToken` to
|
|
306
|
+
override that, or `null` for anonymous. With a webhook secret it serves `POST /_yuva/webhook`, which
|
|
307
|
+
verifies the request and calls `webhooks[type]` like `defineYuvaWebhookHandler`. With an analytics API
|
|
308
|
+
key (`analytics:write`) and property id, `event.context.yuva.track(event)` sends server-side events
|
|
309
|
+
to `yuva.server` (or `NUXT_YUVA_SERVER`), flushed through `waitUntil` after the request where the
|
|
310
|
+
platform has one and when Nitro closes. The secrets are the options `identitySecret`,
|
|
311
|
+
`webhookSecret`, `analytics.apiKey` and `analytics.propertyId` or the variables above; they go only
|
|
312
|
+
into the server runtime config (`useRuntimeConfig().yuva`), never into the public one. A route is
|
|
313
|
+
added only when its secret is known at build time, from `nuxt.config` or the environment; a secret
|
|
314
|
+
that is only in the build environment is not written into the build, so set it at runtime too. A
|
|
315
|
+
route whose secret is empty at runtime answers `404`. `defineYuvaServer` is auto-imported on the
|
|
316
|
+
server (`useyuva/nitro/runtime`), and `event` has your Nuxt's `H3Event` type. Works on Nitro v2 and
|
|
317
|
+
v3. `<YuvaChat />`, `<YuvaPageFeedback />`, `<YuvaDiscussions />` and `<YuvaSubscribe />` are
|
|
277
318
|
auto-imported, render on the server and load their element in the browser when they mount. Props
|
|
278
319
|
are the attributes in camelCase (`pageTitle`, `identityToken`, which also takes a function);
|
|
279
320
|
events are `@rating`, `@feedback`, `@discussion`, `@post`, `@reader` and `@unread`. `yuva-*` tags are custom elements in
|
|
@@ -318,6 +359,207 @@ const { data, error } = await api.GET("/v1/inboxes");
|
|
|
318
359
|
Built on [openapi-fetch](https://openapi-ts.dev/openapi-fetch/); paths, parameters and bodies are
|
|
319
360
|
typed from the contract. Keep API keys on the server.
|
|
320
361
|
|
|
362
|
+
## Server
|
|
363
|
+
|
|
364
|
+
For your backend: sign identity tokens, verify webhooks and send server-side analytics events. It
|
|
365
|
+
uses only Web Crypto, `Request` and `fetch`, so it runs on Node, Bun, Deno and workers, and directly
|
|
366
|
+
under [srvx](https://srvx.h3.dev):
|
|
367
|
+
|
|
368
|
+
```ts
|
|
369
|
+
import { serve } from "srvx";
|
|
370
|
+
import { createAnalytics, signIdentityToken, verifyWebhook, WebhookVerificationError } from "useyuva/server";
|
|
371
|
+
|
|
372
|
+
const analytics = createAnalytics({
|
|
373
|
+
server: "https://support.example.com",
|
|
374
|
+
apiKey: process.env.YUVA_API_KEY!, // analytics:write
|
|
375
|
+
propertyId: process.env.YUVA_ANALYTICS_PROPERTY!,
|
|
376
|
+
onError: (error) => console.warn("yuva analytics", error),
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
serve({
|
|
380
|
+
async fetch(request) {
|
|
381
|
+
const { pathname } = new URL(request.url);
|
|
382
|
+
if (pathname === "/yuva-token") {
|
|
383
|
+
const user = await currentUser(request); // your session
|
|
384
|
+
const token = await signIdentityToken(process.env.YUVA_IDENTITY_SECRET!, {
|
|
385
|
+
subject: user.id, // required: your user id
|
|
386
|
+
email: user.email,
|
|
387
|
+
emailVerified: user.emailConfirmed, // only then does Yuva match the address to a contact
|
|
388
|
+
name: user.name,
|
|
389
|
+
attrs: { plan: user.plan },
|
|
390
|
+
});
|
|
391
|
+
return Response.json({ token });
|
|
392
|
+
}
|
|
393
|
+
if (pathname === "/webhooks/yuva" && request.method === "POST") {
|
|
394
|
+
try {
|
|
395
|
+
const event = await verifyWebhook(process.env.YUVA_WEBHOOK_SECRET!, request);
|
|
396
|
+
// Ignore a webhook-id you have already handled; Yuva retries until it gets a 2xx.
|
|
397
|
+
if (event.type === "message.created" && !event.data.contact?.online) {
|
|
398
|
+
// send your push notification
|
|
399
|
+
}
|
|
400
|
+
return new Response(null, { status: 204 });
|
|
401
|
+
} catch (error) {
|
|
402
|
+
if (error instanceof WebhookVerificationError) return new Response("bad signature", { status: 401 });
|
|
403
|
+
throw error;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
if (pathname === "/signup" && request.method === "POST") {
|
|
407
|
+
// ...
|
|
408
|
+
analytics.track(request, { name: "Signup", props: { plan: "pro" }, revenue: { amount: "29.90", currency: "EUR" } });
|
|
409
|
+
request.waitUntil?.(analytics.flush());
|
|
410
|
+
return Response.redirect(new URL("/welcome", request.url), 303);
|
|
411
|
+
}
|
|
412
|
+
return new Response("Not found", { status: 404 });
|
|
413
|
+
},
|
|
414
|
+
});
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
`signIdentityToken(secret, claims)` returns the HS256 token the widget and the mobile SDKs exchange
|
|
418
|
+
for a contact session, signed with the inbox's identity secret (shown once when the inbox is created
|
|
419
|
+
or its secret is rotated). Claims: `subject` (1 to 200 characters), `email`, `emailVerified`, `name`,
|
|
420
|
+
`locale`, `attrs` (at most 16 KiB as JSON), `id` (the `jti`: Yuva then accepts the token only once),
|
|
421
|
+
`issuedAt` and `ttl` in seconds (300 by default, at most 600). Sign a new one whenever the page or
|
|
422
|
+
app starts a session.
|
|
423
|
+
|
|
424
|
+
`verifyWebhook(secret, request, { tolerance })` checks the [Standard Webhooks](https://www.standardwebhooks.com)
|
|
425
|
+
signature over the raw body with the endpoint's `whsec_…` secret and returns the parsed event, typed
|
|
426
|
+
by its `type`. It throws a `WebhookVerificationError` (`code`: `invalid_secret`, `missing_headers`,
|
|
427
|
+
`invalid_timestamp`, `timestamp_too_old`, `timestamp_too_new`, `invalid_signature`) when no
|
|
428
|
+
signature matches or `webhook-timestamp` is more than `tolerance` seconds (300 by default) from now.
|
|
429
|
+
During a secret rotation either secret verifies. When the body is already read,
|
|
430
|
+
`verifyWebhookBody(secret, headers, bytes, options)` does the same; `signWebhook(secret, id, time, body)`
|
|
431
|
+
signs a request for testing a handler.
|
|
432
|
+
|
|
433
|
+
`createAnalytics({ server, apiKey, propertyId })` reports events only your backend sees, such as a
|
|
434
|
+
signup or a payment, to an analytics property with an API key that has the `analytics:write` scope.
|
|
435
|
+
`track(request, event)` forwards the visitor's address, `User-Agent`, `Sec-CH-UA-Platform`,
|
|
436
|
+
`Sec-CH-UA-Mobile` and `Accept-Language` (never stored by Yuva), so the event counts as the same
|
|
437
|
+
visitor as their browser that day, and takes the URL and referrer from the request when the event
|
|
438
|
+
leaves them out; requests with `DNT: 1` or `Sec-GPC: 1` send nothing. `send(event)` queues an event
|
|
439
|
+
as it is, such as one with your own `visitorKey` (a session id, never an e-mail address) instead of
|
|
440
|
+
`ip` and `userAgent`. Both throw for an event without a name or URL and after `close()`.
|
|
441
|
+
|
|
442
|
+
The visitor's address is read only from `request.ip`, which srvx sets from the connection. Behind a
|
|
443
|
+
proxy, or on a runtime that does not set it, pass `clientIP: (request) => …` returning the address
|
|
444
|
+
your proxy reports, such as `request.headers.get("cf-connecting-ip")` on Cloudflare Workers; a
|
|
445
|
+
forwarded header is trustworthy only when your own proxy sets it.
|
|
446
|
+
|
|
447
|
+
Events are sent in batches of up to 100 every `flushInterval` (5000 ms) or as soon as a batch is
|
|
448
|
+
full, holding at most `queueSize` (10,000) events. Network errors, `429` and `5xx` are retried
|
|
449
|
+
`maxRetries` times (5) with a backoff from `minBackoff` (1000 ms) doubling up to a minute, never
|
|
450
|
+
sooner than `Retry-After`. Dropped events, refused batches (`AnalyticsBatchError`) and events the
|
|
451
|
+
server left out (`AnalyticsEventError`) go to `onError`. The timers never keep a process alive:
|
|
452
|
+
`flush()` sends what is queued and resolves to the last batch error, if any, without rejecting, so
|
|
453
|
+
it can go to `waitUntil`; `close()` flushes and stops the client before the process exits.
|
|
454
|
+
|
|
455
|
+
### h3
|
|
456
|
+
|
|
457
|
+
`useyuva/h3` wraps the same helpers for an [h3](https://h3.dev) v2 app (`h3` is an optional peer
|
|
458
|
+
dependency):
|
|
459
|
+
|
|
460
|
+
```ts
|
|
461
|
+
import { H3, serve } from "h3";
|
|
462
|
+
import { defineYuvaAnalytics, defineYuvaIdentityHandler, defineYuvaWebhookHandler } from "useyuva/h3";
|
|
463
|
+
import { createAnalytics } from "useyuva/server";
|
|
464
|
+
|
|
465
|
+
const app = new H3()
|
|
466
|
+
.use(
|
|
467
|
+
defineYuvaAnalytics({
|
|
468
|
+
analytics: createAnalytics({
|
|
469
|
+
server: "https://support.example.com",
|
|
470
|
+
apiKey: process.env.YUVA_API_KEY!,
|
|
471
|
+
propertyId: process.env.YUVA_ANALYTICS_PROPERTY!,
|
|
472
|
+
}),
|
|
473
|
+
})
|
|
474
|
+
)
|
|
475
|
+
.get(
|
|
476
|
+
"/yuva-token",
|
|
477
|
+
defineYuvaIdentityHandler({
|
|
478
|
+
secret: process.env.YUVA_IDENTITY_SECRET!,
|
|
479
|
+
user: async (event) => {
|
|
480
|
+
const user = await currentUser(event); // your session; null when signed out
|
|
481
|
+
return user && { subject: user.id, email: user.email, emailVerified: user.emailConfirmed, name: user.name };
|
|
482
|
+
},
|
|
483
|
+
})
|
|
484
|
+
)
|
|
485
|
+
.post(
|
|
486
|
+
"/webhooks/yuva",
|
|
487
|
+
defineYuvaWebhookHandler({
|
|
488
|
+
secret: process.env.YUVA_WEBHOOK_SECRET!,
|
|
489
|
+
on: {
|
|
490
|
+
"message.created": async (payload) => {
|
|
491
|
+
if (!payload.data.contact?.online) await sendPush(payload.data);
|
|
492
|
+
},
|
|
493
|
+
"conversation.rated": (payload) => saveRating(payload.data.conversation),
|
|
494
|
+
},
|
|
495
|
+
})
|
|
496
|
+
)
|
|
497
|
+
.post("/signup", async (event) => {
|
|
498
|
+
// ...
|
|
499
|
+
event.context.yuva?.track({ name: "Signup", props: { plan: "pro" } });
|
|
500
|
+
return Response.redirect(new URL("/welcome", event.req.url), 303);
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
serve(app);
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
```html
|
|
507
|
+
<yuva-chat channel="yuva_pk_…" server="https://support.example.com"></yuva-chat>
|
|
508
|
+
<script type="module">
|
|
509
|
+
import "useyuva/chat";
|
|
510
|
+
document.querySelector("yuva-chat").setIdentityToken(async () => {
|
|
511
|
+
const response = await fetch("/yuva-token", { credentials: "same-origin" });
|
|
512
|
+
return response.ok ? (await response.json()).token : null;
|
|
513
|
+
});
|
|
514
|
+
</script>
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
`defineYuvaIdentityHandler({ secret, user, ttl })` answers `{ "token": "…" }` with a token for the
|
|
518
|
+
claims `user(event)` returns, with `Cache-Control: no-store`, and `401` when it returns nothing;
|
|
519
|
+
`ttl` is the default for claims without their own. `defineYuvaWebhookHandler({ secret, tolerance, on })`
|
|
520
|
+
verifies the raw request with `verifyWebhook` and calls the handler for the event's `type` with the
|
|
521
|
+
event typed for it; it answers `401` when the signature does not verify, `204` for a type without a
|
|
522
|
+
handler, and otherwise the `Response` the handler returns or `204`. A handler that throws answers
|
|
523
|
+
`500`, so Yuva retries. `defineYuvaAnalytics({ analytics })` is a middleware that puts
|
|
524
|
+
`track(event)` (`analytics.track` with the request) on `event.context.yuva` and, after a request
|
|
525
|
+
that tracked something, hands `flush()` to the request's `waitUntil`, which srvx sets on every
|
|
526
|
+
runtime it serves; without one the queue goes out on the client's timer.
|
|
527
|
+
|
|
528
|
+
### Nitro
|
|
529
|
+
|
|
530
|
+
`useyuva/nitro` is the same server part for a [Nitro](https://nitro.build) v3 app:
|
|
531
|
+
|
|
532
|
+
```ts
|
|
533
|
+
// nitro.config.ts
|
|
534
|
+
import { defineConfig } from "nitro";
|
|
535
|
+
|
|
536
|
+
export default defineConfig({
|
|
537
|
+
serverDir: "./server",
|
|
538
|
+
modules: ["useyuva/nitro"],
|
|
539
|
+
});
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
```ts
|
|
543
|
+
// server/yuva.ts
|
|
544
|
+
import { getCookie } from "nitro/h3";
|
|
545
|
+
import { defineYuvaServer } from "useyuva/nitro/runtime";
|
|
546
|
+
|
|
547
|
+
export default defineYuvaServer({
|
|
548
|
+
user: async (event) => {
|
|
549
|
+
const user = await findSession(getCookie(event, "session")); // null when signed out
|
|
550
|
+
return user && { subject: user.id, email: user.email, emailVerified: user.emailConfirmed, name: user.name };
|
|
551
|
+
},
|
|
552
|
+
webhooks: { "conversation.rated": (payload) => saveRating(payload.data.conversation) },
|
|
553
|
+
});
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
The variables are `NITRO_YUVA_IDENTITY_SECRET`, `NITRO_YUVA_WEBHOOK_SECRET`,
|
|
557
|
+
`NITRO_YUVA_ANALYTICS_API_KEY`, `NITRO_YUVA_ANALYTICS_PROPERTY_ID` and `NITRO_YUVA_SERVER` (the Yuva
|
|
558
|
+
server analytics goes to), or `yuva({ identitySecret, webhookSecret, analytics: { apiKey, propertyId }, server })`
|
|
559
|
+
from `useyuva/nitro` in `modules`; routes, `track` and the build-time rule are as in [Nuxt](#nuxt).
|
|
560
|
+
`yuva.ts` is read from `serverDir`, or the root without one. The page asks for the token as in the
|
|
561
|
+
h3 example, at `/_yuva/identity`.
|
|
562
|
+
|
|
321
563
|
## License
|
|
322
564
|
|
|
323
565
|
MIT
|
package/dist/esm/chat.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{resolveLocale,catalogFor,translatePlain}from"./index-
|
|
1
|
+
import{resolveLocale,catalogFor,translatePlain}from"./index-9vqd1b2a.js";import{directionOf,addStyles,launcherOf,contrastOn,readStored,writeStored}from"./index-r9f9ttb2.js";var styles=`
|
|
2
2
|
:host {
|
|
3
3
|
--yuva-accent: #2563eb;
|
|
4
4
|
--yuva-on-accent: #ffffff;
|
|
@@ -106,4 +106,4 @@ import{resolveLocale,catalogFor,translatePlain}from"./index-c6q8br7d.js";import{
|
|
|
106
106
|
block-size: 100%;
|
|
107
107
|
min-block-size: 320px;
|
|
108
108
|
}
|
|
109
|
-
`,
|
|
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 fallbackStyle=addStyles(this.#root,styles),head=fallbackStyle?[fallbackStyle]:[];if(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(...head,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(...head,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-025wzr6w.js");define();export{YuvaChatElement,define};
|
package/dist/esm/docs.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{createYuvaClient2}from"./index-6brgxahe.js";import{resolveLocale,translatePlain}from"./index-
|
|
1
|
+
import{createYuvaClient2}from"./index-6brgxahe.js";import{resolveLocale,translatePlain}from"./index-9vqd1b2a.js";import{el}from"./index-rdzxvj2r.js";import{ApiError2}from"./index-gk84nm8f.js";import{directionOf,setStyles,launcherOf,contrastOn,readStored,writeStored}from"./index-r9f9ttb2.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;
|
|
@@ -46,7 +46,7 @@ textarea:focus-visible, input:focus-visible { outline: 2px solid var(--yuva-acce
|
|
|
46
46
|
.send:focus-visible, .link:focus-visible { outline: 2px solid var(--yuva-accent); outline-offset: 2px; }
|
|
47
47
|
.link { padding: 0; border: 0; background: none; color: inherit; font: inherit; text-decoration: underline; cursor: pointer; opacity: 0.8; }
|
|
48
48
|
@media (max-width: 480px) { textarea, input { font-size: 16px; } }
|
|
49
|
-
`;class YuvaDocsElement extends Base{static observedAttributes=["channel","server","locale","dir","page","page-title","identity-token"];root;inbox=null;pageUrl="";refused=!1;direction="ltr";#messages=messages;#client=null;#identity=null;#unfollow=null;#settingsFor="";#connected=!1;#accentColor="";constructor(){super();this.root=this.attachShadow({mode:"open"})}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 resolveLocale(this.getAttribute("locale"),navigator.language)}set locale(value){this.#attribute("locale",value)}get page(){return this.pageUrl||pageOf(this.getAttribute("page"))}set page(value){this.#attribute("page",value)}get pageTitle(){return this.getAttribute("page-title")||document.title}set pageTitle(value){this.#attribute("page-title",value)}#attribute(name,value){if(value===null||value===void 0||value==="")this.removeAttribute(name);else this.setAttribute(name,value)}setIdentityToken(source){if(this.#identity=source,this.#client?.setIdentityToken(()=>this.#identityToken()),this.#connected&&!this.refused)this.render()}connectedCallback(){this.#connected=!0,this.#unfollow??=followPage(()=>this.#syncPage()),this.pageUrl=pageOf(this.getAttribute("page")),this.#applyLocale(),this.#loadSettings(),this.render(),this.pageChanged()}disconnectedCallback(){this.#connected=!1,this.#unfollow?.(),this.#unfollow=null}attributeChangedCallback(name,previous,value){if(!this.#connected||previous===value)return;if(name==="channel"||name==="server"){this.#client?.reset(),this.#client=null,this.refused=!1,this.inbox=null,this.#settingsFor="",this.#loadSettings(),this.reset(),this.render(),this.pageChanged();return}if(name==="page")return this.#syncPage();if(name==="identity-token")this.#client?.setIdentityToken(()=>this.#identityToken());if(name==="locale"||name==="dir")this.#applyLocale();this.render()}t(descriptor){return translatePlain(this.#messages,descriptor,messages)}client(){return this.#client??=createYuvaClient2({server:this.server,channel:this.channel??"",identityToken:()=>this.#identityToken()}),this.#client}get identified(){return!!this.#identity||this.hasAttribute("identity-token")}get canWrite(){return this.identified||this.inbox?.chat.allow_anonymous!==!1}emit(type,detail){this.dispatchEvent(new CustomEvent(type,{detail,bubbles:!0,composed:!0}))}refusal(error,channelRequest=!0){let refused=error instanceof ApiError2&&(error.code==="origin_not_allowed"||error.code==="page_origin"||channelRequest&&error.status===404);if(refused&&!this.refused)this.refused=!0,console.warn(`Yuva: channel ${this.channel} is unknown or does not allow ${this.pageUrl}`),this.root.replaceChildren();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==="anonymous_not_allowed"||code==="invalid_identity_token")return this.t({id:"uuIskV"});return this.t({id:"tAL5wg"})}frame(css,...children){let
|
|
49
|
+
`;class YuvaDocsElement extends Base{static observedAttributes=["channel","server","locale","dir","page","page-title","identity-token"];root;inbox=null;pageUrl="";refused=!1;direction="ltr";#messages=messages;#client=null;#identity=null;#unfollow=null;#settingsFor="";#connected=!1;#accentColor="";constructor(){super();this.root=this.attachShadow({mode:"open"})}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 resolveLocale(this.getAttribute("locale"),navigator.language)}set locale(value){this.#attribute("locale",value)}get page(){return this.pageUrl||pageOf(this.getAttribute("page"))}set page(value){this.#attribute("page",value)}get pageTitle(){return this.getAttribute("page-title")||document.title}set pageTitle(value){this.#attribute("page-title",value)}#attribute(name,value){if(value===null||value===void 0||value==="")this.removeAttribute(name);else this.setAttribute(name,value)}setIdentityToken(source){if(this.#identity=source,this.#client?.setIdentityToken(()=>this.#identityToken()),this.#connected&&!this.refused)this.render()}connectedCallback(){this.#connected=!0,this.#unfollow??=followPage(()=>this.#syncPage()),this.pageUrl=pageOf(this.getAttribute("page")),this.#applyLocale(),this.#loadSettings(),this.render(),this.pageChanged()}disconnectedCallback(){this.#connected=!1,this.#unfollow?.(),this.#unfollow=null}attributeChangedCallback(name,previous,value){if(!this.#connected||previous===value)return;if(name==="channel"||name==="server"){this.#client?.reset(),this.#client=null,this.refused=!1,this.inbox=null,this.#settingsFor="",this.#loadSettings(),this.reset(),this.render(),this.pageChanged();return}if(name==="page")return this.#syncPage();if(name==="identity-token")this.#client?.setIdentityToken(()=>this.#identityToken());if(name==="locale"||name==="dir")this.#applyLocale();this.render()}t(descriptor){return translatePlain(this.#messages,descriptor,messages)}client(){return this.#client??=createYuvaClient2({server:this.server,channel:this.channel??"",identityToken:()=>this.#identityToken()}),this.#client}get identified(){return!!this.#identity||this.hasAttribute("identity-token")}get canWrite(){return this.identified||this.inbox?.chat.allow_anonymous!==!1}emit(type,detail){this.dispatchEvent(new CustomEvent(type,{detail,bubbles:!0,composed:!0}))}refusal(error,channelRequest=!0){let refused=error instanceof ApiError2&&(error.code==="origin_not_allowed"||error.code==="page_origin"||channelRequest&&error.status===404);if(refused&&!this.refused)this.refused=!0,console.warn(`Yuva: channel ${this.channel} is unknown or does not allow ${this.pageUrl}`),this.root.replaceChildren();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==="anonymous_not_allowed"||code==="invalid_identity_token")return this.t({id:"uuIskV"});return this.t({id:"tAL5wg"})}frame(css,...children){let fallback=setStyles(this.root,baseStyles+css),wrapper=el("div","frame");if(wrapper.dir=this.direction,wrapper.lang=this.locale,this.#accentColor)wrapper.style.setProperty("--yuva-accent",this.#accentColor),wrapper.style.setProperty("--yuva-on-accent",contrastOn(this.#accentColor));wrapper.append(...children);let focused=null;try{focused=this.root.activeElement}catch{}if(this.root.replaceChildren(...fallback?[fallback]:[],wrapper),focused instanceof HTMLElement&&focused.isConnected)focused.focus({preventScroll:!0})}async#identityToken(){return this.#identity?await this.#identity()??null:this.getAttribute("identity-token")}#syncPage(){if(!this.#connected)return;let page=pageOf(this.getAttribute("page"));if(page===this.pageUrl)return;this.pageUrl=page,this.reset(),this.render(),this.pageChanged()}#applyLocale(){let locale=this.locale;this.#messages=catalogs[locale]??messages,this.direction=this.getAttribute("dir")==="rtl"?"rtl":this.getAttribute("dir")==="ltr"?"ltr":directionOf(locale)}#loadSettings(){let channel=this.channel,key=`${this.server} ${channel}`;if(!channel||this.#settingsFor===key)return;this.#settingsFor=key;let stored=readStored(channel).launcher;if(stored?.color)this.#accent(stored.color);let request=settings.get(key);if(!request)request=this.client().channelSettings(),settings.set(key,request),request.catch(()=>settings.delete(key));request.then((inbox)=>{if(this.channel!==channel)return;this.inbox=inbox;let launcher=launcherOf(inbox);if(launcher.color)this.#accent(launcher.color);if(writeStored(channel,{...readStored(channel),launcher}),!this.refused)this.render()}).catch((error)=>{if(this.channel===channel)this.refusal(error)})}#accent(color){if(!/^#[0-9a-f]{6}$/i.test(color))return;this.#accentColor=color}}var hashPrefix="#yuva-discussion-",cacheFor=60000,loads=new Map;function forget(prefix){for(let key of loads.keys())if(key.startsWith(prefix))loads.delete(key)}var svg=(path)=>`<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${path}</svg>`,icons={up:svg('<path d="M12 19V5M5 12l7-7 7 7"/>'),comment:svg('<path d="M21 12a8 8 0 01-11.6 7.1L4 20l1-4.6A8 8 0 1121 12z"/>'),check:svg('<path d="M20 6L9 17l-5-5"/>'),lock:svg('<rect x="5" y="11" width="14" height="10" rx="2"/><path d="M8 11V7a4 4 0 018 0v4"/>'),clock:svg('<circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/>')},styles=`
|
|
50
50
|
.frame { display: flex; flex-direction: column; gap: 16px; }
|
|
51
51
|
:host { --yuva-success: #1a7f37; --yuva-warn: #9a6700; }
|
|
52
52
|
@media (prefers-color-scheme: dark) { :host { --yuva-success: #3fb950; --yuva-warn: #d29922; } }
|
|
@@ -110,7 +110,7 @@ button { font: inherit; color: inherit; }
|
|
|
110
110
|
.item .avatar { display: none; }
|
|
111
111
|
.bar .send { inline-size: 100%; }
|
|
112
112
|
}
|
|
113
|
-
`;function hue(text){let hash=0;for(let char of text)hash=hash*31+char.charCodeAt(0)|0;return Math.abs(hash)%360}function excerptOf(html){let template=document.createElement("template");return template.innerHTML=html,(template.content.textContent??"").replace(/\s+/g," ").trim().slice(0,240)}class YuvaDiscussionsElement extends YuvaDocsElement{static loadThread;#s={view:{kind:"list"},items:[],thread:null,threadState:"loading",writer:null,reader:null,busy:new Set,notice:"",error:"",confirm:"",focus:""};#sort="new";#cursor;#listState="loading";#loadedFor="";#offReader=null;#thread=null;#threadLoad=null;#threadStyles="";get discussions(){return this.#s.items}get reader(){return this.#s.reader}#hashChanged=()=>this.#fromHash();connectedCallback(){super.connectedCallback(),window.addEventListener("hashchange",this.#hashChanged),this.#watchReader()}disconnectedCallback(){super.disconnectedCallback(),window.removeEventListener("hashchange",this.#hashChanged),this.#offReader?.(),this.#offReader=null}attributeChangedCallback(name,previous,value){if(super.attributeChangedCallback(name,previous,value),previous===value||!this.isConnected)return;if(name==="channel"||name==="server")this.#offReader?.(),this.#offReader=null,this.#watchReader();else if(name==="identity-token")this.#refreshWriter(!0)}setIdentityToken(source){super.setIdentityToken(source),this.#refreshWriter(!0)}#watchReader(){if(!this.channel||this.#offReader)return;let client=this.client();this.#s.reader=client.reader,this.#offReader=client.on("reader",(reader)=>{this.#s.reader=reader,this.emit("yuva-reader",reader?reader.reader:null),this.#refreshWriter(!0)}),this.#refreshWriter(!1)}async#refreshWriter(reload){if(!this.channel)return;let writer=await this.client().discussionWriter().catch(()=>null),changed=writer!==this.#s.writer;if(this.#s.writer=writer,reload&&changed){if(this.#loadedFor="",this.#load(!1),this.#s.view.kind==="thread")this.#open(this.#s.view.id,!1)}this.render()}reset(){let s=this.#s;s.view={kind:"list"},s.items=[],this.#cursor=void 0,this.#listState="loading",this.#loadedFor="",s.thread=null,s.notice="",s.error="",s.confirm="",this.#thread?.reset()}pageChanged(){this.#load(!1),this.#fromHash()}#linked(){let hash=location.hash;return hash.startsWith(hashPrefix)?decodeURIComponent(hash.slice(hashPrefix.length)):""}#fromHash(){let id=this.#linked();if(!id||!this.channel||this.refused)return;if(this.#s.view.kind==="thread"&&this.#s.view.id===id)return;this.#thread?.close(),this.#s.confirm="",this.#open(id,!0),this.scrollIntoView({block:"start"})}#prefetchOn(button){let prefetch=()=>void this.#loadThread();button.addEventListener("pointerenter",prefetch,{once:!0}),button.addEventListener("focus",prefetch,{once:!0})}#loadThread(){return this.#threadLoad??=YuvaDiscussionsElement.loadThread().then((module)=>{if(this.#threadStyles=module.styles,this.#thread=module.createThread(this.#host),this.#s.view.kind!=="list")this.render();return this.#thread},()=>{if(this.#threadLoad=null,this.#s.view.kind!=="list")this.#s.view={kind:"list"},this.#s.error=this.t({id:"D16wQE"}),this.render();return null}),this.#threadLoad}#host=this.#makeHost();#makeHost(){return{s:this.#s,icons,root:this.root,locale:()=>this.locale,direction:()=>this.direction,page:()=>this.pageUrl,pageTitle:()=>this.pageTitle,mode:()=>this.#mode,client:()=>this.client(),render:()=>this.render(),emit:(type,detail)=>this.emit(type,detail),act:(key,action)=>this.#act(key,action),signIn:()=>this.signIn(),open:(id,focus)=>this.#open(id,focus),toList:()=>this.#toList(),forget:()=>forget(this.#prefix),button:(className,text,key,onClick,html)=>this.#button(className,text,key,onClick,html),avatar:(author,small)=>this.#avatar(author,small),badge:(kind,short)=>this.#badge(kind,short),time:(value)=>this.#time(value),authorName:(author)=>this.#authorName(author),noticeBox:(text,icon)=>this.#noticeBox(text,icon)}}reload(){return this.#load(!0)}open(id){return this.#open(id,!0)}signIn(){return this.#s.error="",this.client().readerSignIn().then(()=>{return}).catch((error)=>{let code=error instanceof ApiError2?error.code:"";if(code==="sign_in_cancelled")return;this.#s.error=code==="popup_blocked"?this.t({id:"nW3go1"}):this.#failure(error),this.render()})}signOut(){return this.client().readerSignOut()}get#prefix(){return`${this.server} ${this.channel} ${this.pageUrl} `}async#load(force,more=!1){let page=this.pageUrl;if(!this.channel||!page||this.refused)return;let reader=this.client().reader,auth=reader?reader.token.slice(-12):this.#s.writer??"-",key=`${this.#prefix}${this.#sort} ${auth}`;if(!force&&!more&&this.#loadedFor===key)return;this.#loadedFor=key;let request;if(more)request=this.client().listDiscussions(page,{sort:this.#sort,cursor:this.#cursor});else{let load=loads.get(key);if(force||!load||Date.now()-load.at>cacheFor){load={at:Date.now(),request:this.client().listDiscussions(page,{sort:this.#sort})},loads.set(key,load);let current=load;load.request.catch(()=>loads.get(key)===current&&loads.delete(key))}if(request=load.request,!this.#s.items.length)this.#listState="loading"}if(more)this.#s.busy.add("more");this.render();try{let result=await request;if(this.#loadedFor!==key)return;this.#s.items=more?[...this.#s.items,...result.items.filter((d)=>!this.#s.items.some((i)=>i.id===d.id))]:result.items,this.#cursor=result.next_cursor,this.#listState="ready"}catch(error){if(this.#loadedFor!==key||this.refusal(error))return;if(!more)this.#loadedFor="";if(this.#listState=error instanceof ApiError2&&error.code==="discussions_off"?"off":more?"ready":"error",more)this.#s.error=this.#failure(error)}finally{this.#s.busy.delete("more")}this.render()}async#open(id,focus){let s=this.#s,page=this.pageUrl,known=s.thread?.discussion.id===id?s.thread:null;this.#loadThread(),s.view={kind:"thread",id};let listed=s.items.find((d)=>d.id===id);if(s.thread=known??(listed?{discussion:listed,posts:[]}:null),s.threadState=known?"ready":"loading",focus)s.notice=known?s.notice:"",s.error="",s.focus="thread-title";this.render();try{let detail=await this.client().getDiscussion(id);if(this.pageUrl!==page||s.view.kind!=="thread"||s.view.id!==id)return;let titled=!!s.thread;if(s.thread=detail,s.threadState="ready",focus&&!titled)s.focus="thread-title";s.items=s.items.map((d)=>d.id===id?detail.discussion:d)}catch(error){if(this.pageUrl!==page||s.view.kind!=="thread"||s.view.id!==id)return;if(this.refusal(error,!1))return;s.threadState=error instanceof ApiError2&&error.status===404?"missing":"error"}this.render()}#toList(){let s=this.#s;if(this.#linked()){let url=new URL(location.href);url.hash="",history.replaceState(history.state,"",url.href)}s.view={kind:"list"},this.#thread?.close(),s.confirm="",s.error="",s.focus=s.thread?`open-${s.thread.discussion.id}`:"start",this.render(),this.#load(!1)}#failure(error){let code=error instanceof ApiError2?error.code:"";if(code==="sign_in_required")return this.t({id:"LwDe8g"});if(code==="discussion_locked")return this.t({id:"KEjhwb"});if(code==="edit_window_closed")return this.t({id:"b_f4xj"});if(code==="discussions_off")return this.t({id:"tayT8P"});if(code==="forbidden")return this.t({id:"8Ekoev"});if(code==="validation_failed")return this.t({id:"Su3Vyp"});return this.errorText(error)}async#act(key,action){let s=this.#s;if(s.busy.has(key))return;s.busy.add(key),s.error="",this.render();try{return await action()}catch(error){if(!this.refusal(error,!1))s.error=this.#failure(error);if(error instanceof ApiError2&&error.code==="sign_in_required")this.#refreshWriter(!1);return}finally{s.busy.delete(key),this.render()}}get#mode(){return this.inbox?.chat.discussions??"moderated"}#button(className,text,key,onClick,html=""){let button=el("button",className);if(button.type="button",html)button.innerHTML=html;return button.append(text),button.dataset.key=key,button.addEventListener("click",onClick),button}#authorName(author){if(author.deleted)return author.kind==="member"?this.t({id:"-UxBoG"}):this.t({id:"FMWGhb"});if(author.kind==="anonymous")return this.t({id:"D9CFV5"});return author.name||(author.kind==="member"?this.t({id:"nLqMRA"}):this.t({id:"zmA6sS"}))}#avatar(author,small=!1){let avatar=el("span",`avatar${small?" small":""}`,author.deleted?"":author.initials||"?");if(avatar.setAttribute("aria-hidden","true"),author.deleted||author.kind==="anonymous")avatar.classList.add("gone");else if(author.kind==="member")avatar.classList.add("team");else avatar.style.setProperty("--avatar",`hsl(${hue(author.name||author.initials)} 45% 42%)`);return avatar}#badge(kind,short=!1){let text=kind==="answered"?this.t({id:"GqCA_t"}):kind==="pending"?short?this.t({id:"UbRKMZ"}):this.t({id:"lWv8id"}):kind==="team"?this.t({id:"KM6m8p"}):this.t({id:"G2fuEb"}),badge=el("span",`badge ${kind}`);if(kind!=="team")badge.innerHTML=kind==="answered"?icons.check:kind==="pending"?icons.clock:icons.lock;return badge.append(text),badge}#time(value){let time=el("time");time.dateTime=value;let date=new Date(value);time.title=date.toLocaleString(this.locale);let seconds=(date.getTime()-Date.now())/1000,steps=[["second",60],["minute",60],["hour",24],["day",30],["month",12],["year",1/0]],amount=seconds;for(let[unit,size]of steps){if(Math.abs(amount)<size){time.textContent=unit==="second"?this.t({id:"xOTzt5"}):new Intl.RelativeTimeFormat(this.locale,{numeric:"auto"}).format(Math.round(amount),unit);break}amount/=size}return time}#header(){let head=el("div","head");head.append(el("h2","",this.t({id:"7oGBMv"})));let who=el("div","who"),reader=this.#s.reader;if(reader){let name=reader.reader.name||this.t({id:"zmA6sS"});who.append(this.#avatar({kind:"reader",name,initials:reader.reader.initials,deleted:!1},!0)),who.append(el("span","",name)),who.append(this.#button("plain",this.t({id:"fcWrnU"}),"sign-out",()=>void this.signOut()))}else if(!this.#s.writer&&this.#mode!=="off")who.append(this.#button("ghost",this.t({id:"5lWFkC"}),"sign-in",()=>void this.signIn()));return head.append(who),head}#listView(){let nodes=[],bar=el("div","bar"),tabs=el("div","tabs");tabs.setAttribute("role","tablist"),tabs.setAttribute("aria-label",this.t({id:"78dEKp"}));let sorts=[["new",this.t({id:"isRobC"})],["top",this.t({id:"WWy8TN"})],["unanswered",this.t({id:"2lF8Zj"})]];sorts.forEach(([sort,label],index)=>{let tab=this.#button("tab",label,`sort-${sort}`,()=>this.#setSort(sort));tab.setAttribute("role","tab"),tab.setAttribute("aria-selected",String(this.#sort===sort)),tab.setAttribute("aria-controls","yuva-discussion-list"),tab.tabIndex=this.#sort===sort?0:-1,tab.addEventListener("keydown",(event)=>{let step=event.key==="ArrowRight"?1:event.key==="ArrowLeft"?-1:0;if(!step)return;event.preventDefault();let forward=this.direction==="rtl"?-step:step,next=sorts[(index+forward+sorts.length)%sorts.length];if(next)this.#setSort(next[0])}),tabs.append(tab)});let start=this.#button("send",this.t({id:"z1qf6l"}),"start",()=>{this.#loadThread(),this.#s.view={kind:"new"},this.#s.error="",this.#s.notice="",this.#s.focus="new-title",this.render()});if(this.#prefetchOn(start),bar.append(tabs,start),nodes.push(bar),this.#listState==="loading"&&!this.#s.items.length){let skeleton=el("div","skeleton");return skeleton.setAttribute("aria-busy","true"),skeleton.setAttribute("aria-label",this.t({id:"pzatAP"})),nodes.push(skeleton),nodes}if(this.#listState==="error"&&!this.#s.items.length){let box=el("div","empty");box.append(el("p","",this.t({id:"MDmQKn"})));let retry=this.#button("ghost",this.t({id:"KDw4GX"}),"retry",()=>void this.#load(!0));return retry.style.marginBlockStart="8px",box.append(retry),nodes.push(box),nodes}if(!this.#s.items.length){let box=el("div","empty");if(box.append(el("p","",this.#sort==="unanswered"?this.t({id:"twEQ41"}):this.t({id:"rpfOwz"}))),this.#sort!=="unanswered")box.append(el("p","muted",this.t({id:"b6Orhy"})));return nodes.push(box),nodes}let list=el("ul","list");list.id="yuva-discussion-list",list.setAttribute("aria-label",this.t({id:"j-3eim"})),list.setAttribute("aria-busy",String(this.#listState==="loading"));for(let discussion of this.#s.items){let item=el("li","item");item.append(this.#avatar(discussion.author));let main=el("div","main"),title=this.#button("title",discussion.title,`open-${discussion.id}`,()=>void this.#open(discussion.id,!0));title.dir="auto",this.#prefetchOn(title),main.append(title);let excerpt=discussion.deleted?"":excerptOf(discussion.body_html);if(excerpt){let text=el("p","excerpt",excerpt);text.dir="auto",main.append(text)}let meta=el("div","meta");if(meta.append(el("span","",this.#authorName(discussion.author)),this.#time(discussion.created_at)),discussion.author.kind==="member"&&!discussion.author.deleted)meta.append(this.#badge("team"));if(discussion.answer_post_id)meta.append(this.#badge("answered"));if(discussion.state==="pending")meta.append(this.#badge("pending"));if(discussion.locked)meta.append(this.#badge("locked"));main.append(meta);let counts=el("div","counts"),votes=el("span","count");votes.innerHTML=icons.up,votes.append(String(discussion.votes)),votes.setAttribute("aria-label",this.t({id:"8ntmc1",values:{0:discussion.votes}})),votes.title=votes.getAttribute("aria-label")??"";let replies=el("span","count");replies.innerHTML=icons.comment,replies.append(String(discussion.reply_count)),replies.setAttribute("aria-label",this.t({id:"Hw3P48",values:{0:discussion.reply_count}})),replies.title=replies.getAttribute("aria-label")??"",counts.append(votes,replies),item.append(main,counts),list.append(item)}if(nodes.push(list),this.#cursor){let more=this.#button("ghost more",this.#s.busy.has("more")?this.t({id:"Pwqkdw"}):this.t({id:"fMPkxb"}),"more",()=>void this.#load(!1,!0));more.disabled=this.#s.busy.has("more"),nodes.push(more)}return nodes}#setSort(sort){if(this.#sort===sort)return;this.#sort=sort,this.#s.items=[],this.#cursor=void 0,this.#listState="loading",this.#s.focus=`sort-${sort}`,this.#load(!1)}#noticeBox(text,icon=icons.clock){let box=el("div","notice");return box.setAttribute("role","status"),box.innerHTML=icon,box.append(el("p","",text)),box}render(){if(this.refused||!this.channel){this.root.replaceChildren();return}let focused="";try{focused=this.root.activeElement?.dataset?.key??""}catch{}let children=[this.#header()];if(this.#mode==="off"||this.#listState==="off")children.push(el("p","muted",this.t({id:"tayT8P"})));else if(this.#s.view.kind!=="list"){let thread=this.#thread;if(thread)children.push(...this.#s.view.kind==="thread"?thread.threadView():thread.newView());else{let skeleton=el("div","skeleton");skeleton.setAttribute("aria-busy","true"),skeleton.setAttribute("aria-label",this.t({id:"Pwqkdw"})),children.push(skeleton)}}else{if(this.#s.notice)children.push(this.#noticeBox(this.#s.notice));children.push(...this.#listView())}if(this.#s.error){let error=el("p","error",this.#s.error);error.setAttribute("role","alert"),children.push(error)}if(this.frame(styles+this.#threadStyles,...children),this.#s.view.kind!=="list"&&!this.#thread)return;this.#thread?.syncSend();let wanted=this.#s.focus;this.#s.focus="";let key=wanted||focused;if(!key)return;let active=null;try{active=this.root.activeElement}catch{}if(active instanceof HTMLElement&&active.dataset.key===key)return;[...this.root.querySelectorAll("[data-key]")].find((node)=>node.dataset.key===key)?.focus({preventScroll:!wanted})}}var maxRemembered=500,thumb=(down)=>`<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"${down?' style="transform:scaleY(-1)"':""}><path d="M7 10v11H3V10zM7 10l4-8a3 3 0 013 3v4h6a2 2 0 012 2.3l-1.4 8A2 2 0 0118.6 21H7"/></svg>`,styles2=`
|
|
113
|
+
`;function hue(text){let hash=0;for(let char of text)hash=hash*31+char.charCodeAt(0)|0;return Math.abs(hash)%360}function excerptOf(html){let template=document.createElement("template");return template.innerHTML=html,(template.content.textContent??"").replace(/\s+/g," ").trim().slice(0,240)}class YuvaDiscussionsElement extends YuvaDocsElement{static loadThread;#s={view:{kind:"list"},items:[],thread:null,threadState:"loading",writer:null,reader:null,busy:new Set,notice:"",error:"",confirm:"",focus:""};#sort="new";#cursor;#listState="loading";#loadedFor="";#offReader=null;#thread=null;#threadLoad=null;#threadStyles="";get discussions(){return this.#s.items}get reader(){return this.#s.reader}#hashChanged=()=>this.#fromHash();connectedCallback(){super.connectedCallback(),window.addEventListener("hashchange",this.#hashChanged),this.#watchReader()}disconnectedCallback(){super.disconnectedCallback(),window.removeEventListener("hashchange",this.#hashChanged),this.#offReader?.(),this.#offReader=null}attributeChangedCallback(name,previous,value){if(super.attributeChangedCallback(name,previous,value),previous===value||!this.isConnected)return;if(name==="channel"||name==="server")this.#offReader?.(),this.#offReader=null,this.#watchReader();else if(name==="identity-token")this.#refreshWriter(!0)}setIdentityToken(source){super.setIdentityToken(source),this.#refreshWriter(!0)}#watchReader(){if(!this.channel||this.#offReader)return;let client=this.client();this.#s.reader=client.reader,this.#offReader=client.on("reader",(reader)=>{this.#s.reader=reader,this.emit("yuva-reader",reader?reader.reader:null),this.#refreshWriter(!0)}),this.#refreshWriter(!1)}async#refreshWriter(reload){if(!this.channel)return;let writer=await this.client().discussionWriter().catch(()=>null),changed=writer!==this.#s.writer;if(this.#s.writer=writer,reload&&changed){if(this.#loadedFor="",this.#load(!1),this.#s.view.kind==="thread")this.#open(this.#s.view.id,!1)}this.render()}reset(){let s=this.#s;s.view={kind:"list"},s.items=[],this.#cursor=void 0,this.#listState="loading",this.#loadedFor="",s.thread=null,s.notice="",s.error="",s.confirm="",this.#thread?.reset()}pageChanged(){this.#load(!1),this.#fromHash()}#linked(){let hash=location.hash;return hash.startsWith(hashPrefix)?decodeURIComponent(hash.slice(hashPrefix.length)):""}#fromHash(){let id=this.#linked();if(!id||!this.channel||this.refused)return;if(this.#s.view.kind==="thread"&&this.#s.view.id===id)return;this.#thread?.close(),this.#s.confirm="",this.#open(id,!0),this.scrollIntoView({block:"start"})}#prefetchOn(button){let prefetch=()=>void this.#loadThread();button.addEventListener("pointerenter",prefetch,{once:!0}),button.addEventListener("focus",prefetch,{once:!0})}#loadThread(){return this.#threadLoad??=YuvaDiscussionsElement.loadThread().then((module)=>{if(this.#threadStyles=module.styles,this.#thread=module.createThread(this.#host),this.#s.view.kind!=="list")this.render();return this.#thread},()=>{if(this.#threadLoad=null,this.#s.view.kind!=="list")this.#s.view={kind:"list"},this.#s.error=this.t({id:"D16wQE"}),this.render();return null}),this.#threadLoad}#host=this.#makeHost();#makeHost(){return{s:this.#s,icons,root:this.root,locale:()=>this.locale,direction:()=>this.direction,page:()=>this.pageUrl,pageTitle:()=>this.pageTitle,mode:()=>this.#mode,client:()=>this.client(),render:()=>this.render(),emit:(type,detail)=>this.emit(type,detail),act:(key,action)=>this.#act(key,action),signIn:()=>this.signIn(),open:(id,focus)=>this.#open(id,focus),toList:()=>this.#toList(),forget:()=>forget(this.#prefix),button:(className,text,key,onClick,html)=>this.#button(className,text,key,onClick,html),avatar:(author,small)=>this.#avatar(author,small),badge:(kind,short)=>this.#badge(kind,short),time:(value)=>this.#time(value),authorName:(author)=>this.#authorName(author),noticeBox:(text,icon)=>this.#noticeBox(text,icon)}}reload(){return this.#load(!0)}open(id){return this.#open(id,!0)}signIn(){return this.#s.error="",this.client().readerSignIn().then(()=>{return}).catch((error)=>{let code=error instanceof ApiError2?error.code:"";if(code==="sign_in_cancelled")return;this.#s.error=code==="popup_blocked"?this.t({id:"nW3go1"}):this.#failure(error),this.render()})}signOut(){return this.client().readerSignOut()}get#prefix(){return`${this.server} ${this.channel} ${this.pageUrl} `}async#load(force,more=!1){let page=this.pageUrl;if(!this.channel||!page||this.refused)return;let reader=this.client().reader,auth=reader?reader.token.slice(-12):this.#s.writer??"-",key=`${this.#prefix}${this.#sort} ${auth}`;if(!force&&!more&&this.#loadedFor===key)return;this.#loadedFor=key;let request;if(more)request=this.client().listDiscussions(page,{sort:this.#sort,cursor:this.#cursor});else{let load=loads.get(key);if(force||!load||Date.now()-load.at>cacheFor){load={at:Date.now(),request:this.client().listDiscussions(page,{sort:this.#sort})},loads.set(key,load);let current=load;load.request.catch(()=>loads.get(key)===current&&loads.delete(key))}if(request=load.request,!this.#s.items.length)this.#listState="loading"}if(more)this.#s.busy.add("more");this.render();try{let result=await request;if(this.#loadedFor!==key)return;this.#s.items=more?[...this.#s.items,...result.items.filter((d)=>!this.#s.items.some((i)=>i.id===d.id))]:result.items,this.#cursor=result.next_cursor,this.#listState="ready"}catch(error){if(this.#loadedFor!==key||this.refusal(error))return;if(!more)this.#loadedFor="";if(this.#listState=error instanceof ApiError2&&error.code==="discussions_off"?"off":more?"ready":"error",more)this.#s.error=this.#failure(error)}finally{this.#s.busy.delete("more")}this.render()}async#open(id,focus){let s=this.#s,page=this.pageUrl,known=s.thread?.discussion.id===id?s.thread:null;this.#loadThread(),s.view={kind:"thread",id};let listed=s.items.find((d)=>d.id===id);if(s.thread=known??(listed?{discussion:listed,posts:[]}:null),s.threadState=known?"ready":"loading",focus)s.notice=known?s.notice:"",s.error="",s.focus="thread-title";this.render();try{let detail=await this.client().getDiscussion(id);if(this.pageUrl!==page||s.view.kind!=="thread"||s.view.id!==id)return;let titled=!!s.thread;if(s.thread=detail,s.threadState="ready",focus&&!titled)s.focus="thread-title";s.items=s.items.map((d)=>d.id===id?detail.discussion:d)}catch(error){if(this.pageUrl!==page||s.view.kind!=="thread"||s.view.id!==id)return;if(this.refusal(error,!1))return;s.threadState=error instanceof ApiError2&&error.status===404?"missing":"error"}this.render()}#toList(){let s=this.#s;if(this.#linked()){let url=new URL(location.href);url.hash="",history.replaceState(history.state,"",url.href)}s.view={kind:"list"},this.#thread?.close(),s.confirm="",s.error="",s.focus=s.thread?`open-${s.thread.discussion.id}`:"start",this.render(),this.#load(!1)}#failure(error){let code=error instanceof ApiError2?error.code:"";if(code==="sign_in_required")return this.t({id:"LwDe8g"});if(code==="discussion_locked")return this.t({id:"KEjhwb"});if(code==="edit_window_closed")return this.t({id:"b_f4xj"});if(code==="discussions_off")return this.t({id:"tayT8P"});if(code==="forbidden")return this.t({id:"8Ekoev"});if(code==="validation_failed")return this.t({id:"Su3Vyp"});return this.errorText(error)}async#act(key,action){let s=this.#s;if(s.busy.has(key))return;s.busy.add(key),s.error="",this.render();try{return await action()}catch(error){if(!this.refusal(error,!1))s.error=this.#failure(error);if(error instanceof ApiError2&&error.code==="sign_in_required")this.#refreshWriter(!1);return}finally{s.busy.delete(key),this.render()}}get#mode(){return this.inbox?.chat.discussions??"moderated"}#button(className,text,key,onClick,html=""){let button=el("button",className);if(button.type="button",html)button.innerHTML=html;return button.append(text),button.dataset.key=key,button.addEventListener("click",onClick),button}#authorName(author){if(author.deleted)return author.kind==="member"?this.t({id:"-UxBoG"}):this.t({id:"FMWGhb"});if(author.kind==="anonymous")return this.t({id:"D9CFV5"});return author.name||(author.kind==="member"?this.t({id:"nLqMRA"}):this.t({id:"zmA6sS"}))}#avatar(author,small=!1){let avatar=el("span",`avatar${small?" small":""}`,author.deleted?"":author.initials||"?");if(avatar.setAttribute("aria-hidden","true"),author.deleted||author.kind==="anonymous")avatar.classList.add("gone");else if(author.kind==="member")avatar.classList.add("team");else avatar.style.setProperty("--avatar",`hsl(${hue(author.name||author.initials)} 45% 42%)`);return avatar}#badge(kind,short=!1){let text=kind==="answered"?this.t({id:"GqCA_t"}):kind==="pending"?short?this.t({id:"UbRKMZ"}):this.t({id:"lWv8id"}):kind==="team"?this.t({id:"KM6m8p"}):this.t({id:"G2fuEb"}),badge=el("span",`badge ${kind}`);if(kind!=="team")badge.innerHTML=kind==="answered"?icons.check:kind==="pending"?icons.clock:icons.lock;return badge.append(text),badge}#time(value){let time=el("time");time.dateTime=value;let date=new Date(value);time.title=date.toLocaleString(this.locale);let seconds=(date.getTime()-Date.now())/1000,steps=[["second",60],["minute",60],["hour",24],["day",30],["month",12],["year",1/0]],amount=seconds;for(let[unit,size]of steps){if(Math.abs(amount)<size){time.textContent=unit==="second"?this.t({id:"xOTzt5"}):new Intl.RelativeTimeFormat(this.locale,{numeric:"auto"}).format(Math.round(amount),unit);break}amount/=size}return time}#header(){let head=el("div","head");head.append(el("h2","",this.t({id:"7oGBMv"})));let who=el("div","who"),reader=this.#s.reader;if(reader){let name=reader.reader.name||this.t({id:"zmA6sS"});who.append(this.#avatar({kind:"reader",name,initials:reader.reader.initials,deleted:!1},!0)),who.append(el("span","",name)),who.append(this.#button("plain",this.t({id:"fcWrnU"}),"sign-out",()=>void this.signOut()))}else if(!this.#s.writer&&this.#mode!=="off")who.append(this.#button("ghost",this.t({id:"5lWFkC"}),"sign-in",()=>void this.signIn()));return head.append(who),head}#listView(){let nodes=[],bar=el("div","bar"),tabs=el("div","tabs");tabs.setAttribute("role","tablist"),tabs.setAttribute("aria-label",this.t({id:"78dEKp"}));let sorts=[["new",this.t({id:"isRobC"})],["top",this.t({id:"WWy8TN"})],["unanswered",this.t({id:"2lF8Zj"})]];sorts.forEach(([sort,label],index)=>{let tab=this.#button("tab",label,`sort-${sort}`,()=>this.#setSort(sort));tab.setAttribute("role","tab"),tab.setAttribute("aria-selected",String(this.#sort===sort)),tab.setAttribute("aria-controls","yuva-discussion-list"),tab.tabIndex=this.#sort===sort?0:-1,tab.addEventListener("keydown",(event)=>{let step=event.key==="ArrowRight"?1:event.key==="ArrowLeft"?-1:0;if(!step)return;event.preventDefault();let forward=this.direction==="rtl"?-step:step,next=sorts[(index+forward+sorts.length)%sorts.length];if(next)this.#setSort(next[0])}),tabs.append(tab)});let start=this.#button("send",this.t({id:"z1qf6l"}),"start",()=>{this.#loadThread(),this.#s.view={kind:"new"},this.#s.error="",this.#s.notice="",this.#s.focus="new-title",this.render()});if(this.#prefetchOn(start),bar.append(tabs,start),nodes.push(bar),this.#listState==="loading"&&!this.#s.items.length){let skeleton=el("div","skeleton");return skeleton.setAttribute("aria-busy","true"),skeleton.setAttribute("aria-label",this.t({id:"pzatAP"})),nodes.push(skeleton),nodes}if(this.#listState==="error"&&!this.#s.items.length){let box=el("div","empty");box.append(el("p","",this.t({id:"MDmQKn"})));let retry=this.#button("ghost",this.t({id:"KDw4GX"}),"retry",()=>void this.#load(!0));return retry.style.marginBlockStart="8px",box.append(retry),nodes.push(box),nodes}if(!this.#s.items.length){let box=el("div","empty");if(box.append(el("p","",this.#sort==="unanswered"?this.t({id:"twEQ41"}):this.t({id:"rpfOwz"}))),this.#sort!=="unanswered")box.append(el("p","muted",this.t({id:"b6Orhy"})));return nodes.push(box),nodes}let list=el("ul","list");list.id="yuva-discussion-list",list.setAttribute("aria-label",this.t({id:"j-3eim"})),list.setAttribute("aria-busy",String(this.#listState==="loading"));for(let discussion of this.#s.items){let item=el("li","item");item.append(this.#avatar(discussion.author));let main=el("div","main"),title=this.#button("title",discussion.title,`open-${discussion.id}`,()=>void this.#open(discussion.id,!0));title.dir="auto",this.#prefetchOn(title),main.append(title);let excerpt=discussion.deleted?"":excerptOf(discussion.body_html);if(excerpt){let text=el("p","excerpt",excerpt);text.dir="auto",main.append(text)}let meta=el("div","meta");if(meta.append(el("span","",this.#authorName(discussion.author)),this.#time(discussion.created_at)),discussion.author.kind==="member"&&!discussion.author.deleted)meta.append(this.#badge("team"));if(discussion.answer_post_id)meta.append(this.#badge("answered"));if(discussion.state==="pending")meta.append(this.#badge("pending"));if(discussion.locked)meta.append(this.#badge("locked"));main.append(meta);let counts=el("div","counts"),votes=el("span","count");votes.innerHTML=icons.up,votes.append(String(discussion.votes)),votes.setAttribute("aria-label",this.t({id:"8ntmc1",values:{0:discussion.votes}})),votes.title=votes.getAttribute("aria-label")??"";let replies=el("span","count");replies.innerHTML=icons.comment,replies.append(String(discussion.reply_count)),replies.setAttribute("aria-label",this.t({id:"Hw3P48",values:{0:discussion.reply_count}})),replies.title=replies.getAttribute("aria-label")??"",counts.append(votes,replies),item.append(main,counts),list.append(item)}if(nodes.push(list),this.#cursor){let more=this.#button("ghost more",this.#s.busy.has("more")?this.t({id:"Pwqkdw"}):this.t({id:"fMPkxb"}),"more",()=>void this.#load(!1,!0));more.disabled=this.#s.busy.has("more"),nodes.push(more)}return nodes}#setSort(sort){if(this.#sort===sort)return;this.#sort=sort,this.#s.items=[],this.#cursor=void 0,this.#listState="loading",this.#s.focus=`sort-${sort}`,this.#load(!1)}#noticeBox(text,icon=icons.clock){let box=el("div","notice");return box.setAttribute("role","status"),box.innerHTML=icon,box.append(el("p","",text)),box}render(){if(this.refused||!this.channel){this.root.replaceChildren();return}let focused="";try{focused=this.root.activeElement?.dataset?.key??""}catch{}let children=[this.#header()];if(this.#mode==="off"||this.#listState==="off")children.push(el("p","muted",this.t({id:"tayT8P"})));else if(this.#s.view.kind!=="list"){let thread=this.#thread;if(thread)children.push(...this.#s.view.kind==="thread"?thread.threadView():thread.newView());else{let skeleton=el("div","skeleton");skeleton.setAttribute("aria-busy","true"),skeleton.setAttribute("aria-label",this.t({id:"Pwqkdw"})),children.push(skeleton)}}else{if(this.#s.notice)children.push(this.#noticeBox(this.#s.notice));children.push(...this.#listView())}if(this.#s.error){let error=el("p","error",this.#s.error);error.setAttribute("role","alert"),children.push(error)}if(this.frame(styles+this.#threadStyles,...children),this.#s.view.kind!=="list"&&!this.#thread)return;this.#thread?.syncSend();let wanted=this.#s.focus;this.#s.focus="";let key=wanted||focused;if(!key)return;let active=null;try{active=this.root.activeElement}catch{}if(active instanceof HTMLElement&&active.dataset.key===key)return;[...this.root.querySelectorAll("[data-key]")].find((node)=>node.dataset.key===key)?.focus({preventScroll:!wanted})}}var maxRemembered=500,thumb=(down)=>`<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path${down?' transform="matrix(1 0 0 -1 0 24)"':""} d="M7 10v11H3V10zM7 10l4-8a3 3 0 013 3v4h6a2 2 0 012 2.3l-1.4 8A2 2 0 0118.6 21H7"/></svg>`,styles2=`
|
|
114
114
|
.frame { display: flex; flex-direction: column; gap: 12px; }
|
|
115
115
|
.ask { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 16px; }
|
|
116
116
|
.question { font-weight: 600; }
|
|
@@ -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-
|
|
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-gx5pssds.js");defineDocs();export{YuvaDiscussionsElement,YuvaPageFeedbackElement,defineDocs};
|
package/dist/esm/h3.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{signIdentityToken2,WebhookVerificationError2,verifyWebhook2}from"./index-qcd538se.js";import{defineHandler,defineMiddleware}from"h3";var noStore={"cache-control":"no-store"};function defineYuvaIdentityHandler({secret,user,ttl}){return defineHandler(async(event)=>{let claims=await user(event);if(!claims)return new Response(null,{status:401,headers:noStore});let token=await signIdentityToken2(secret,{...claims,ttl:claims.ttl??ttl});return Response.json({token},{headers:noStore})})}function defineYuvaWebhookHandler({secret,tolerance,on}){return defineHandler(async(event)=>{let payload;try{payload=await verifyWebhook2(secret,event.req,{tolerance})}catch(error){if(error instanceof WebhookVerificationError2)return new Response(error.message,{status:401});throw error}if(!Object.hasOwn(on,payload.type))return new Response(null,{status:204});let handler=on[payload.type],result=await handler?.(payload,event);return result instanceof Response?result:new Response(null,{status:204})})}function defineYuvaAnalytics({analytics}){return defineMiddleware(async(event,next)=>{let request=event.req,tracked=!1;event.context.yuva={track(e){analytics.track(request,e),tracked=!0}};try{return await next()}finally{if(tracked&&request.waitUntil)event.waitUntil(analytics.flush())}})}export{defineYuvaAnalytics,defineYuvaIdentityHandler,defineYuvaWebhookHandler};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{translate,localeIn}from"./index-
|
|
1
|
+
import{translate,localeIn}from"./index-r9f9ttb2.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
2
|
export{resolveLocale,catalogFor,translatePlain};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var encoder=new TextEncoder,utf8=(text)=>encoder.encode(text);function base64(bytes){let binary="";for(let byte of bytes)binary+=String.fromCharCode(byte);return btoa(binary)}var base64url=(bytes)=>base64(bytes).replace(/=+$/,"").replace(/\+/g,"-").replace(/\//g,"_"),strictBase64=/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;function decodeBase64(text){if(!strictBase64.test(text))return;return Uint8Array.from(atob(text),(char)=>char.charCodeAt(0))}var hmacKey=(key,usages)=>crypto.subtle.importKey("raw",key,{name:"HMAC",hash:"SHA-256"},!1,usages),goEscapes={"<":"\\u003c",">":"\\u003e","&":"\\u0026","\u2028":"\\u2028","\u2029":"\\u2029"};function goJSON(value){if(value===null||typeof value!=="object"||typeof value.toJSON==="function")return JSON.stringify(value)?.replace(/[<>&\u2028\u2029]/g,(char)=>goEscapes[char]);if(Array.isArray(value))return`[${value.map((item)=>goJSON(item)??"null").join(",")}]`;let fields=[];for(let key of Object.keys(value).sort()){let encoded=goJSON(value[key]);if(encoded!==void 0)fields.push(`${goJSON(key)}:${encoded}`)}return`{${fields.join(",")}}`}var DEFAULT_IDENTITY_TTL2=300,MAX_IDENTITY_TTL2=600,header=base64url(utf8('{"alg":"HS256","typ":"JWT"}'));async function signIdentityToken2(secret,claims){if(!secret)throw Error("identity: the inbox identity secret is empty");let sub=(claims.subject??"").trim();if(!sub||[...sub].length>200)throw Error("identity: subject must be 1 to 200 characters");let ttl=claims.ttl||DEFAULT_IDENTITY_TTL2;if(!(ttl>0&&ttl<=MAX_IDENTITY_TTL2))throw Error("identity: ttl must be at most 10 minutes");let at=(claims.issuedAt??new Date).getTime(),payload={sub,email:claims.email||void 0,email_verified:claims.emailVerified||void 0,name:claims.name||void 0,locale:claims.locale||void 0,attrs:claims.attrs&&Object.keys(claims.attrs).length>0?claims.attrs:void 0,jti:claims.id||void 0,iat:Math.floor(at/1000),exp:Math.floor((at+ttl*1000)/1000)},fields=Object.entries(payload).flatMap(([key,value])=>{let encoded=goJSON(value);return encoded===void 0?[]:[`"${key}":${encoded}`]}),signing=`${header}.${base64url(utf8(`{${fields.join(",")}}`))}`,key=await hmacKey(utf8(secret),["sign"]);return`${signing}.${base64url(new Uint8Array(await crypto.subtle.sign("HMAC",key,utf8(signing))))}`}var DEFAULT_WEBHOOK_TOLERANCE2=300,messages={invalid_secret:"webhook: the secret is not a whsec_ base64 secret",missing_headers:"webhook: webhook-id, webhook-timestamp or webhook-signature is missing",invalid_timestamp:"webhook: webhook-timestamp is not a unix time",timestamp_too_old:"webhook: webhook-timestamp is too old",timestamp_too_new:"webhook: webhook-timestamp is too far in the future",invalid_signature:"webhook: no signature matches"};class WebhookVerificationError2 extends Error{code;constructor(code){super(messages[code]);this.name="WebhookVerificationError",this.code=code}}function key(secret){let bytes=decodeBase64(secret.trim().replace(/^whsec_/,""));if(!bytes||bytes.length===0)throw new WebhookVerificationError2("invalid_secret");return bytes}function signed(id,timestamp,body){let prefix=utf8(`${id}.${timestamp}.`),data=new Uint8Array(prefix.length+body.length);return data.set(prefix),data.set(body,prefix.length),data}async function signWebhook2(secret,id,timestamp,body){let k=await hmacKey(key(secret),["sign"]),data=signed(id,String(Math.floor(timestamp.getTime()/1000)),typeof body==="string"?utf8(body):body);return`v1,${base64(new Uint8Array(await crypto.subtle.sign("HMAC",k,data)))}`}async function verifyWebhookBody2(secret,headers,body,{tolerance,now=new Date}={}){let raw=key(secret),id=headers.get("webhook-id"),ts=headers.get("webhook-timestamp"),sigs=headers.get("webhook-signature");if(!id||!ts||!sigs)throw new WebhookVerificationError2("missing_headers");if(!/^[+-]?\d{1,19}$/.test(ts))throw new WebhookVerificationError2("invalid_timestamp");let window=(tolerance&&tolerance>0?tolerance:DEFAULT_WEBHOOK_TOLERANCE2)*1000,at=Number(ts)*1000;if(now.getTime()-at>window)throw new WebhookVerificationError2("timestamp_too_old");if(at-now.getTime()>window)throw new WebhookVerificationError2("timestamp_too_new");let k=await hmacKey(raw,["verify"]),data=signed(id,ts,body);for(let sig of sigs.split(/\s+/)){let comma=sig.indexOf(",");if(comma<0||sig.slice(0,comma)!=="v1")continue;let got=decodeBase64(sig.slice(comma+1));if(got&&got.length>0&&await crypto.subtle.verify("HMAC",k,got,data))return}throw new WebhookVerificationError2("invalid_signature")}async function verifyWebhook2(secret,request,options){let body=new Uint8Array(await request.arrayBuffer());return await verifyWebhookBody2(secret,request.headers,body,options),JSON.parse(new TextDecoder().decode(body))}
|
|
2
|
+
export{DEFAULT_IDENTITY_TTL2,MAX_IDENTITY_TTL2,signIdentityToken2,DEFAULT_WEBHOOK_TOLERANCE2,WebhookVerificationError2,signWebhook2,verifyWebhookBody2,verifyWebhook2};
|
|
@@ -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"}var sheets=new Map;function sheetFor(css){if(typeof CSSStyleSheet>"u"||!("replaceSync"in CSSStyleSheet.prototype))return null;let sheet=sheets.get(css);if(!sheet)sheet=new CSSStyleSheet,sheet.replaceSync(css),sheets.set(css,sheet);return sheet}function fallback(css){let style=document.createElement("style");return style.textContent=css,style}function addStyles(root,css){let sheet=sheetFor(css);if(!sheet||!(root instanceof ShadowRoot))return fallback(css);if(!root.adoptedStyleSheets.includes(sheet))root.adoptedStyleSheets=[...root.adoptedStyleSheets,sheet];return null}function setStyles(root,css){let sheet=sheetFor(css);if(!sheet)return fallback(css);return root.adoptedStyleSheets=[sheet],null}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,addStyles,setStyles,launcherOf,contrastOn,readStored,writeStored};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import {
|
|
3
|
+
findServerFile,
|
|
4
|
+
IDENTITY_ROUTE,
|
|
5
|
+
runtimePath,
|
|
6
|
+
SERVER_MODULE,
|
|
7
|
+
serverModule,
|
|
8
|
+
serverParts,
|
|
9
|
+
serverRuntimeConfig,
|
|
10
|
+
WEBHOOK_ROUTE
|
|
11
|
+
} from "./setup.js";
|
|
12
|
+
export function yuva(options = {}) {
|
|
13
|
+
return {
|
|
14
|
+
name: "useyuva",
|
|
15
|
+
setup(nitro) {
|
|
16
|
+
const config = serverRuntimeConfig(nitro.options.runtimeConfig.yuva, options);
|
|
17
|
+
nitro.options.runtimeConfig.yuva = config;
|
|
18
|
+
const parts = serverParts(config, process.env, "NITRO_");
|
|
19
|
+
if (!parts.identity && !parts.webhook && !parts.analytics)
|
|
20
|
+
return;
|
|
21
|
+
const dir = nitro.options.serverDir ? resolve(nitro.options.rootDir, nitro.options.serverDir) : nitro.options.rootDir;
|
|
22
|
+
const server = findServerFile(dir);
|
|
23
|
+
if (!server && parts.identity)
|
|
24
|
+
nitro.logger.warn(`useyuva: no yuva.ts in ${dir}, so ${IDENTITY_ROUTE} answers 404`);
|
|
25
|
+
nitro.options.virtual[SERVER_MODULE] = serverModule({
|
|
26
|
+
nitro: 3,
|
|
27
|
+
server,
|
|
28
|
+
runtimeConfig: "nitro/runtime-config"
|
|
29
|
+
});
|
|
30
|
+
if (parts.identity)
|
|
31
|
+
nitro.options.handlers.push({ route: IDENTITY_ROUTE, method: "GET", handler: runtimePath("identity") });
|
|
32
|
+
if (parts.webhook)
|
|
33
|
+
nitro.options.handlers.push({ route: WEBHOOK_ROUTE, method: "POST", handler: runtimePath("webhook") });
|
|
34
|
+
if (parts.analytics)
|
|
35
|
+
nitro.options.plugins.push(runtimePath("plugin"));
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export default yuva();
|