cursedbelt 4.7.0 → 5.0.1
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 +36 -0
- package/dist/react/chat/index.d.ts +1 -0
- package/dist/react/chat/index.d.ts.map +1 -1
- package/dist/react/chat/index.js +1 -0
- package/dist/react/chat/index.js.map +1 -1
- package/dist/react/chat/usePresence.d.ts +75 -0
- package/dist/react/chat/usePresence.d.ts.map +1 -0
- package/dist/react/chat/usePresence.js +109 -0
- package/dist/react/chat/usePresence.js.map +1 -0
- package/dist/react/components/SecretInput.d.ts +7 -2
- package/dist/react/components/SecretInput.d.ts.map +1 -1
- package/dist/react/components/SecretInput.js +5 -2
- package/dist/react/components/SecretInput.js.map +1 -1
- package/dist/react/components/TooltipImpl.d.ts.map +1 -1
- package/dist/react/components/TooltipImpl.js +52 -3
- package/dist/react/components/TooltipImpl.js.map +1 -1
- package/dist/react/emoji/EmojiPicker.d.ts.map +1 -1
- package/dist/react/emoji/EmojiPicker.js +49 -8
- package/dist/react/emoji/EmojiPicker.js.map +1 -1
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +1 -0
- package/dist/react/index.js.map +1 -1
- package/dist/react/lib/form.d.ts +2 -2
- package/dist/react/lib/form.d.ts.map +1 -1
- package/dist/react/lib/form.js +4 -41
- package/dist/react/lib/form.js.map +1 -1
- package/dist/react/lib/zodResolver.d.ts +9 -0
- package/dist/react/lib/zodResolver.d.ts.map +1 -0
- package/dist/react/lib/zodResolver.js +105 -0
- package/dist/react/lib/zodResolver.js.map +1 -0
- package/dist/react/master-lock/MasterLockDialog.d.ts.map +1 -1
- package/dist/react/master-lock/MasterLockDialog.js +2 -1
- package/dist/react/master-lock/MasterLockDialog.js.map +1 -1
- package/dist/react/master-lock/MasterLockProvider.d.ts +12 -18
- package/dist/react/master-lock/MasterLockProvider.d.ts.map +1 -1
- package/dist/react/master-lock/MasterLockProvider.js +14 -20
- package/dist/react/master-lock/MasterLockProvider.js.map +1 -1
- package/dist/react/media/MediaUploadControl.d.ts +3 -7
- package/dist/react/media/MediaUploadControl.d.ts.map +1 -1
- package/dist/react/media/MediaUploadControl.js +4 -13
- package/dist/react/media/MediaUploadControl.js.map +1 -1
- package/dist/react/media/fileUploadClient.d.ts +0 -8
- package/dist/react/media/fileUploadClient.d.ts.map +1 -1
- package/dist/react/media/fileUploadClient.js +0 -8
- package/dist/react/media/fileUploadClient.js.map +1 -1
- package/dist/react/media/hlsSource.d.ts.map +1 -1
- package/dist/react/media/hlsSource.js +4 -2
- package/dist/react/media/hlsSource.js.map +1 -1
- package/dist/react/media/index.d.ts +0 -4
- package/dist/react/media/index.d.ts.map +1 -1
- package/dist/react/media/index.js +0 -4
- package/dist/react/media/index.js.map +1 -1
- package/dist/react/sessionAuth.d.ts +1 -2
- package/dist/react/sessionAuth.d.ts.map +1 -1
- package/dist/react/sessionAuth.js +2 -3
- package/dist/react/sessionAuth.js.map +1 -1
- package/dist/scripts/guardrailsEnforce.d.ts.map +1 -1
- package/dist/styles-areas/activity-bar.css +1 -1
- package/dist/styles-areas/analytics.css +1 -1
- package/dist/styles-areas/auth.css +1 -1
- package/dist/styles-areas/charts.css +1 -1
- package/dist/styles-areas/chat.css +1 -1
- package/dist/styles-areas/core.css +1 -1
- package/dist/styles-areas/dashboard-grid.css +1 -1
- package/dist/styles-areas/data-table.css +1 -1
- package/dist/styles-areas/deep-link.css +1 -1
- package/dist/styles-areas/disk-usage.css +1 -1
- package/dist/styles-areas/emoji.css +1 -1
- package/dist/styles-areas/fields.css +1 -1
- package/dist/styles-areas/file-tree.css +1 -1
- package/dist/styles-areas/keep-awake.css +1 -1
- package/dist/styles-areas/layout-engine.css +1 -1
- package/dist/styles-areas/markdown.css +1 -1
- package/dist/styles-areas/master-detail.css +1 -1
- package/dist/styles-areas/master-lock.css +108 -2
- package/dist/styles-areas/media-gallery.css +1 -1
- package/dist/styles-areas/media.css +1 -17
- package/dist/styles-areas/nav.css +1 -1
- package/dist/styles-areas/notifications.css +1 -1
- package/dist/styles-areas/overlays.css +1 -1
- package/dist/styles-areas/palette.css +1 -1
- package/dist/styles-areas/spreadsheet.css +1 -1
- package/dist/styles-areas/virtual.css +1 -1
- package/dist/styles-areas/wizard.css +1 -1
- package/dist/styles-areas/workbench.css +1 -1
- package/dist/styles-areas/workbook-viewer.css +1 -1
- package/dist/styles-utilities.css +0 -16
- package/dist/testing/_noBun.d.ts +2 -0
- package/dist/testing/_noBun.d.ts.map +1 -0
- package/dist/testing/_noBun.js +10 -0
- package/dist/testing/_noBun.js.map +1 -0
- package/package.json +54 -20
- package/scripts/checkAreaStyles.spec.ts +71 -0
- package/scripts/checkAreaStyles.ts +150 -0
- package/scripts/guardrailsEnforce.spec.ts +43 -0
- package/scripts/guardrailsEnforce.ts +46 -1
- package/scripts/styleAreas.ts +1 -2
- package/src/barrelsReachNoOptionalPeer.spec.ts +6 -3
- package/src/bundlerOnlyEntries.spec.ts +101 -0
- package/src/declaredImports.spec.ts +4 -0
- package/src/optionalPeersTable.spec.ts +127 -0
- package/src/react/chat/index.ts +8 -0
- package/src/react/chat/usePresence.spec.tsx +138 -0
- package/src/react/chat/usePresence.ts +185 -0
- package/src/react/components/SecretInput.tsx +12 -1
- package/src/react/components/TooltipImpl.tsx +54 -5
- package/src/react/emoji/EmojiPicker.tsx +70 -19
- package/src/react/index.ts +8 -0
- package/src/react/lib/form.ts +5 -48
- package/src/react/lib/zodResolver.spec.ts +82 -0
- package/src/react/lib/zodResolver.ts +136 -0
- package/src/react/master-lock/MasterLockDialog.tsx +13 -9
- package/src/react/master-lock/MasterLockProvider.spec.tsx +17 -0
- package/src/react/master-lock/MasterLockProvider.tsx +16 -19
- package/src/react/media/MediaUploadControl.tsx +8 -29
- package/src/react/media/fileUploadClient.ts +7 -17
- package/src/react/media/hlsSource.ts +4 -1
- package/src/react/media/index.ts +0 -16
- package/src/react/sessionAuth.ts +2 -3
- package/src/scriptsResolve.spec.ts +66 -0
- package/src/styles-areas/activity-bar.css +1 -1
- package/src/styles-areas/analytics.css +1 -1
- package/src/styles-areas/auth.css +1 -1
- package/src/styles-areas/charts.css +1 -1
- package/src/styles-areas/chat.css +1 -1
- package/src/styles-areas/core.css +1 -1
- package/src/styles-areas/dashboard-grid.css +1 -1
- package/src/styles-areas/data-table.css +1 -1
- package/src/styles-areas/deep-link.css +1 -1
- package/src/styles-areas/disk-usage.css +1 -1
- package/src/styles-areas/emoji.css +1 -1
- package/src/styles-areas/fields.css +1 -1
- package/src/styles-areas/file-tree.css +1 -1
- package/src/styles-areas/keep-awake.css +1 -1
- package/src/styles-areas/layout-engine.css +1 -1
- package/src/styles-areas/markdown.css +1 -1
- package/src/styles-areas/master-detail.css +1 -1
- package/src/styles-areas/master-lock.css +108 -2
- package/src/styles-areas/media-gallery.css +1 -1
- package/src/styles-areas/media.css +1 -17
- package/src/styles-areas/nav.css +1 -1
- package/src/styles-areas/notifications.css +1 -1
- package/src/styles-areas/overlays.css +1 -1
- package/src/styles-areas/palette.css +1 -1
- package/src/styles-areas/spreadsheet.css +1 -1
- package/src/styles-areas/virtual.css +1 -1
- package/src/styles-areas/wizard.css +1 -1
- package/src/styles-areas/workbench.css +1 -1
- package/src/styles-areas/workbook-viewer.css +1 -1
- package/src/styles-utilities.css +0 -16
- package/src/testing/_noBun.ts +12 -0
- package/src/unpeeredStaysOffTheBarrel.spec.ts +99 -0
- package/src/verifyGraph.spec.ts +11 -0
- package/dist/react/media/PrivateIndexView.d.ts +0 -6
- package/dist/react/media/PrivateIndexView.d.ts.map +0 -1
- package/dist/react/media/PrivateIndexView.js +0 -44
- package/dist/react/media/PrivateIndexView.js.map +0 -1
- package/dist/react/media/PrivateModeBadge.d.ts +0 -17
- package/dist/react/media/PrivateModeBadge.d.ts.map +0 -1
- package/dist/react/media/PrivateModeBadge.js +0 -24
- package/dist/react/media/PrivateModeBadge.js.map +0 -1
- package/dist/react/media/privateClient.d.ts +0 -34
- package/dist/react/media/privateClient.d.ts.map +0 -1
- package/dist/react/media/privateClient.js +0 -36
- package/dist/react/media/privateClient.js.map +0 -1
- package/dist/react/media/usePrivateUnlock.d.ts +0 -25
- package/dist/react/media/usePrivateUnlock.d.ts.map +0 -1
- package/dist/react/media/usePrivateUnlock.js +0 -68
- package/dist/react/media/usePrivateUnlock.js.map +0 -1
- package/src/react/media/PrivateIndexView.tsx +0 -113
- package/src/react/media/PrivateModeBadge.tsx +0 -50
- package/src/react/media/privateClient.spec.ts +0 -113
- package/src/react/media/privateClient.ts +0 -82
- package/src/react/media/usePrivateUnlock.ts +0 -132
package/README.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# cursedbelt
|
|
2
|
+
|
|
3
|
+
The React design system of the cursedbelt split — components, styles, theme. The tiers:
|
|
4
|
+
|
|
5
|
+
| package | what it is |
|
|
6
|
+
| --- | --- |
|
|
7
|
+
| `cursedbelt-core` | runtime-free types, schemas and wire shapes both sides share |
|
|
8
|
+
| `cursedbelt` (this one) | React components, `styles.css`, `theme.css`, the guardrails scanner |
|
|
9
|
+
| `cursedbelt-server` | the server tier — Bun routes, storage, auth, master lock |
|
|
10
|
+
| `cursedbelt-cc` | the parked cc platform (verify pipeline, studios) |
|
|
11
|
+
|
|
12
|
+
The full reference — every subpath, the stylesheet routes, and what each subpath makes you
|
|
13
|
+
install — is [`docs/API-REFERENCE.md`](docs/API-REFERENCE.md). The rules a component must keep are
|
|
14
|
+
[`docs/STANDARDS.md`](docs/STANDARDS.md).
|
|
15
|
+
|
|
16
|
+
## Before you import it — the traps that have cost a build
|
|
17
|
+
|
|
18
|
+
1. **A subpath can need a package you have not installed.** `cursedbelt/react/charts` needs
|
|
19
|
+
`recharts`, `cursedbelt/react/rich-text` the `@tiptap/*` set, and so on: they are *optional*
|
|
20
|
+
peers, so `bun add cursedbelt` does not bring them. Skip one and Vite fails on a module called
|
|
21
|
+
`__vite-optional-peer-dep:recharts:cursedbelt` — the package to add is the middle field. The
|
|
22
|
+
table is "What you install, and what you don't" in `docs/API-REFERENCE.md`, and
|
|
23
|
+
`src/optionalPeersTable.spec.ts` fails this repo's gate when it stops matching the code.
|
|
24
|
+
2. **Import the leaf, not a barrel, for one thing.** `cursedbelt/react` is the one barrel that is
|
|
25
|
+
safe whole — `src/barrelsReachNoOptionalPeer.spec.ts` proves it reaches no optional peer. On the
|
|
26
|
+
server tier the rule is the opposite: `cursedbelt-server`'s root export is a barrel, so an app
|
|
27
|
+
takes a leaf such as `cursedbelt-server/login-throttle`. That package's own docs carry which
|
|
28
|
+
leaves exist and what its barrel costs.
|
|
29
|
+
3. **`Bun.serve`'s `websocket` option selects an overload** — it is a `cursedbelt-server` seam and
|
|
30
|
+
its note lives there.
|
|
31
|
+
|
|
32
|
+
## Working on it
|
|
33
|
+
|
|
34
|
+
`bun run verify` is the gate (typecheck ∥ lint ∥ tests ∥ build ∥ Playwright on the demo), and
|
|
35
|
+
`bun run demo` is the kitchen-sink app every browser spec drives. Publishing is
|
|
36
|
+
`tools/publish-libs.sh` at the generation root, which runs this gate first.
|
|
@@ -6,5 +6,6 @@ export { MessageScroller, type MessageScrollerProps } from './MessageScroller.js
|
|
|
6
6
|
export { MessageThread, type MessageThreadProps } from './MessageThread.js';
|
|
7
7
|
export { PresenceRoster, type PresenceRosterProps } from './PresenceRoster.js';
|
|
8
8
|
export { StatusMarker, type StatusMarkerProps } from './StatusMarker.js';
|
|
9
|
+
export { PRESENCE_RETRY_MS, type PresenceEntryLike, type PresenceSubscription, presenceUsers, type UsePresenceOptions, usePresence, } from './usePresence.js';
|
|
9
10
|
export type { ChatAttachment, ChatAuthor, ChatMessage, MessageStatus, PresenceUser, } from './types.js';
|
|
10
11
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/react/chat/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAC3E,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,KAAK,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAClF,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,KAAK,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAClF,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC/E,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACzE,YAAY,EACV,cAAc,EACd,UAAU,EACV,WAAW,EACX,aAAa,EACb,YAAY,GACb,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/react/chat/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAC3E,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,KAAK,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAClF,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,KAAK,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAClF,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC/E,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACzE,OAAO,EACL,iBAAiB,EACjB,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EACzB,aAAa,EACb,KAAK,kBAAkB,EACvB,WAAW,GACZ,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,cAAc,EACd,UAAU,EACV,WAAW,EACX,aAAa,EACb,YAAY,GACb,MAAM,YAAY,CAAC"}
|
package/dist/react/chat/index.js
CHANGED
|
@@ -6,4 +6,5 @@ export { MessageScroller } from './MessageScroller.js';
|
|
|
6
6
|
export { MessageThread } from './MessageThread.js';
|
|
7
7
|
export { PresenceRoster } from './PresenceRoster.js';
|
|
8
8
|
export { StatusMarker } from './StatusMarker.js';
|
|
9
|
+
export { PRESENCE_RETRY_MS, presenceUsers, usePresence, } from './usePresence.js';
|
|
9
10
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/react/chat/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAwC,MAAM,aAAa,CAAC;AAC3E,OAAO,EAAE,UAAU,EAAwB,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAE,eAAe,EAA6B,MAAM,sBAAsB,CAAC;AAClF,OAAO,EAAE,UAAU,EAAwB,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAE,eAAe,EAA6B,MAAM,sBAAsB,CAAC;AAClF,OAAO,EAAE,aAAa,EAA2B,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAAE,cAAc,EAA4B,MAAM,qBAAqB,CAAC;AAC/E,OAAO,EAAE,YAAY,EAA0B,MAAM,mBAAmB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/react/chat/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAwC,MAAM,aAAa,CAAC;AAC3E,OAAO,EAAE,UAAU,EAAwB,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAE,eAAe,EAA6B,MAAM,sBAAsB,CAAC;AAClF,OAAO,EAAE,UAAU,EAAwB,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAE,eAAe,EAA6B,MAAM,sBAAsB,CAAC;AAClF,OAAO,EAAE,aAAa,EAA2B,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAAE,cAAc,EAA4B,MAAM,qBAAqB,CAAC;AAC/E,OAAO,EAAE,YAAY,EAA0B,MAAM,mBAAmB,CAAC;AACzE,OAAO,EACL,iBAAiB,EAGjB,aAAa,EAEb,WAAW,GACZ,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { type WsLocation } from 'cursedbelt-core/ws-wire';
|
|
2
|
+
import type { PresenceUser } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Who else is here — the CLIENT half of presence, so `PresenceRoster` has something to render.
|
|
5
|
+
*
|
|
6
|
+
* Task 078 (owner, 2026-09-19: *"show which other users are on apps"*). The library shipped the
|
|
7
|
+
* view (`PresenceRoster`) and the server (`cursedbelt-server`'s `createWsHub` with
|
|
8
|
+
* `presence: true`, whose `getPresence(channel)` is one `{ userId, connectedAt }` row per live
|
|
9
|
+
* client) and nothing in between — so the roster sat exported with zero consumers, and the one
|
|
10
|
+
* app that shows presence (`apps/family`, `src/live.ts`) hand-rolled its own socket. This is that
|
|
11
|
+
* socket, generalised: open `/ws/<channel>`, take every `presence` snapshot the channel
|
|
12
|
+
* broadcasts, and reconnect with a capped backoff when the socket drops.
|
|
13
|
+
*
|
|
14
|
+
* The server side a channel needs is one line in its lifecycle hooks:
|
|
15
|
+
*
|
|
16
|
+
* onConnect: (_c, hub) => hub.broadcast('rooms', 'presence', hub.getPresence('rooms')),
|
|
17
|
+
* onDisconnect: (_c, hub) => hub.broadcast('rooms', 'presence', hub.getPresence('rooms')),
|
|
18
|
+
*
|
|
19
|
+
* ── What it does with a snapshot ─────────────────────────────────────────────────────────
|
|
20
|
+
* · ONE row per user, not per tab — the same person in three tabs is one avatar — keeping the
|
|
21
|
+
* earliest `connectedAt`, so the roster's order is "who arrived first" and stable.
|
|
22
|
+
* · `selfId` drops the viewer: the roster is who ELSE is here.
|
|
23
|
+
* · `resolveUser` turns an id into a name/avatar; the hub deliberately ships only the id (its
|
|
24
|
+
* `PresenceEntry` is the "business-safe view" of a client). A row may also carry `name` /
|
|
25
|
+
* `avatarUrl` itself, for a channel that broadcasts richer rows.
|
|
26
|
+
*
|
|
27
|
+
* ── A dropped socket is an EMPTY roster, not a stale one ─────────────────────────────────
|
|
28
|
+
* On close the users go to `[]` and `connected` to false until the next snapshot. Showing the
|
|
29
|
+
* last list while disconnected would tell the viewer someone is here who may have left an hour
|
|
30
|
+
* ago; an empty strip is merely less informative. Same choice as family's `useLiveTree`.
|
|
31
|
+
*/
|
|
32
|
+
/** One presence row as a hub channel broadcasts it (`cursedbelt-server`'s `PresenceEntry`). */
|
|
33
|
+
export interface PresenceEntryLike {
|
|
34
|
+
userId: string;
|
|
35
|
+
connectedAt?: number;
|
|
36
|
+
name?: string;
|
|
37
|
+
avatarUrl?: string;
|
|
38
|
+
}
|
|
39
|
+
export interface UsePresenceOptions {
|
|
40
|
+
/** The hub channel (`/ws/<channel>`) whose snapshots to follow. */
|
|
41
|
+
channel: string;
|
|
42
|
+
/** Room within the channel (`?room=`), when the channel is roomed. */
|
|
43
|
+
roomId?: string;
|
|
44
|
+
/** Off ⇒ no socket at all (e.g. signed out). Default true. */
|
|
45
|
+
enabled?: boolean;
|
|
46
|
+
/** The viewer's own id — left out of the roster. */
|
|
47
|
+
selfId?: string | null;
|
|
48
|
+
/** Name and avatar for an id the snapshot does not carry them for. */
|
|
49
|
+
resolveUser?: (userId: string) => {
|
|
50
|
+
name?: string;
|
|
51
|
+
avatarUrl?: string;
|
|
52
|
+
} | undefined;
|
|
53
|
+
/** The frame event carrying the snapshot. Default `'presence'`. */
|
|
54
|
+
event?: string;
|
|
55
|
+
/** Reconnect backoff, capped at its last entry. */
|
|
56
|
+
retryMs?: readonly number[];
|
|
57
|
+
/** Test/SSR seams. Default `window.location` and the global `WebSocket`. */
|
|
58
|
+
location?: WsLocation;
|
|
59
|
+
createSocket?: (url: string) => WebSocket;
|
|
60
|
+
}
|
|
61
|
+
export interface PresenceSubscription {
|
|
62
|
+
/** Everybody else on the channel now — pass straight to `<PresenceRoster users={…}>`. */
|
|
63
|
+
users: PresenceUser[];
|
|
64
|
+
/** Is the socket open? A closed one also means `users` is `[]`. */
|
|
65
|
+
connected: boolean;
|
|
66
|
+
}
|
|
67
|
+
/** Backoff, capped. A tab left open overnight must not hammer the box. */
|
|
68
|
+
export declare const PRESENCE_RETRY_MS: readonly [500, 1000, 2000, 5000, 10000, 30000];
|
|
69
|
+
/**
|
|
70
|
+
* Collapse a snapshot to one {@link PresenceUser} per user, earliest arrival first, without
|
|
71
|
+
* `selfId`. Pure — the hook's whole judgement, exported so it is tested without a socket.
|
|
72
|
+
*/
|
|
73
|
+
export declare function presenceUsers(rows: readonly PresenceEntryLike[], selfId?: string | null, resolveUser?: UsePresenceOptions['resolveUser']): PresenceUser[];
|
|
74
|
+
export declare function usePresence(options: UsePresenceOptions): PresenceSubscription;
|
|
75
|
+
//# sourceMappingURL=usePresence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usePresence.d.ts","sourceRoot":"","sources":["../../../src/react/chat/usePresence.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyC,KAAK,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAEjG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,+FAA+F;AAC/F,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,mEAAmE;IACnE,OAAO,EAAE,MAAM,CAAC;IAChB,sEAAsE;IACtE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8DAA8D;IAC9D,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,oDAAoD;IACpD,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,sEAAsE;IACtE,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;IACpF,mEAAmE;IACnE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,UAAU,CAAC;IACtB,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,SAAS,CAAC;CAC3C;AAED,MAAM,WAAW,oBAAoB;IACnC,yFAAyF;IACzF,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,mEAAmE;IACnE,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,0EAA0E;AAC1E,eAAO,MAAM,iBAAiB,gDAAsD,CAAC;AAErF;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,SAAS,iBAAiB,EAAE,EAClC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,EACtB,WAAW,CAAC,EAAE,kBAAkB,CAAC,aAAa,CAAC,GAC9C,YAAY,EAAE,CAqBhB;AAcD,wBAAgB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,oBAAoB,CAqE7E"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { buildChannelSocketUrl, parseWsMessage } from 'cursedbelt-core/ws-wire';
|
|
2
|
+
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
+
/** Backoff, capped. A tab left open overnight must not hammer the box. */
|
|
4
|
+
export const PRESENCE_RETRY_MS = [500, 1_000, 2_000, 5_000, 10_000, 30_000];
|
|
5
|
+
/**
|
|
6
|
+
* Collapse a snapshot to one {@link PresenceUser} per user, earliest arrival first, without
|
|
7
|
+
* `selfId`. Pure — the hook's whole judgement, exported so it is tested without a socket.
|
|
8
|
+
*/
|
|
9
|
+
export function presenceUsers(rows, selfId, resolveUser) {
|
|
10
|
+
const byUser = new Map();
|
|
11
|
+
for (const row of rows) {
|
|
12
|
+
if (!row || typeof row.userId !== 'string' || row.userId === '')
|
|
13
|
+
continue;
|
|
14
|
+
if (selfId != null && row.userId === selfId)
|
|
15
|
+
continue;
|
|
16
|
+
const seen = byUser.get(row.userId);
|
|
17
|
+
if (!seen || (row.connectedAt ?? Infinity) < (seen.connectedAt ?? Infinity))
|
|
18
|
+
byUser.set(row.userId, row);
|
|
19
|
+
}
|
|
20
|
+
return [...byUser.values()]
|
|
21
|
+
.sort((a, b) => (a.connectedAt ?? Infinity) - (b.connectedAt ?? Infinity))
|
|
22
|
+
.map((row) => {
|
|
23
|
+
const resolved = resolveUser?.(row.userId);
|
|
24
|
+
const name = row.name ?? resolved?.name;
|
|
25
|
+
const avatarUrl = row.avatarUrl ?? resolved?.avatarUrl;
|
|
26
|
+
return {
|
|
27
|
+
id: row.userId,
|
|
28
|
+
isOnline: true,
|
|
29
|
+
...(name !== undefined ? { name } : {}),
|
|
30
|
+
...(avatarUrl !== undefined ? { avatarUrl } : {}),
|
|
31
|
+
};
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
/** The rows in a snapshot frame: the hub's bare array, or `{ users }` / `{ peers }` / `{ presence }`. */
|
|
35
|
+
function rowsOf(data) {
|
|
36
|
+
if (Array.isArray(data))
|
|
37
|
+
return data;
|
|
38
|
+
if (data && typeof data === 'object') {
|
|
39
|
+
for (const key of ['users', 'peers', 'presence']) {
|
|
40
|
+
const value = data[key];
|
|
41
|
+
if (Array.isArray(value))
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
export function usePresence(options) {
|
|
48
|
+
const { channel, roomId, enabled = true, selfId = null, resolveUser, event = 'presence', retryMs = PRESENCE_RETRY_MS, location, createSocket, } = options;
|
|
49
|
+
const [rows, setRows] = useState([]);
|
|
50
|
+
const [connected, setConnected] = useState(false);
|
|
51
|
+
// The seams and backoff are read through a ref so an inline lambda or array literal in the
|
|
52
|
+
// caller does not tear the socket down on every render.
|
|
53
|
+
const seams = useRef({ location, createSocket, retryMs });
|
|
54
|
+
seams.current = { location, createSocket, retryMs };
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
const loc = seams.current.location ?? (typeof window !== 'undefined' ? window.location : undefined);
|
|
57
|
+
if (!enabled || !loc)
|
|
58
|
+
return;
|
|
59
|
+
let closed = false;
|
|
60
|
+
let attempt = 0;
|
|
61
|
+
let retry = null;
|
|
62
|
+
let socket = null;
|
|
63
|
+
const open = () => {
|
|
64
|
+
if (closed)
|
|
65
|
+
return;
|
|
66
|
+
const url = buildChannelSocketUrl(channel, roomId, loc);
|
|
67
|
+
const make = seams.current.createSocket ?? ((u) => new WebSocket(u));
|
|
68
|
+
const ws = make(url);
|
|
69
|
+
socket = ws;
|
|
70
|
+
ws.addEventListener('open', () => {
|
|
71
|
+
attempt = 0;
|
|
72
|
+
setConnected(true);
|
|
73
|
+
});
|
|
74
|
+
ws.addEventListener('message', (message) => {
|
|
75
|
+
const frame = parseWsMessage(String(message.data));
|
|
76
|
+
if (!frame || frame.event !== event)
|
|
77
|
+
return;
|
|
78
|
+
const next = rowsOf(frame.data);
|
|
79
|
+
if (next)
|
|
80
|
+
setRows(next);
|
|
81
|
+
});
|
|
82
|
+
ws.addEventListener('close', () => {
|
|
83
|
+
setConnected(false);
|
|
84
|
+
setRows([]);
|
|
85
|
+
if (closed)
|
|
86
|
+
return;
|
|
87
|
+
const steps = seams.current.retryMs;
|
|
88
|
+
const wait = steps[Math.min(attempt, steps.length - 1)] ?? 30_000;
|
|
89
|
+
attempt += 1;
|
|
90
|
+
retry = setTimeout(open, wait);
|
|
91
|
+
});
|
|
92
|
+
// An `error` is always followed by a `close`; reconnecting from both would schedule two.
|
|
93
|
+
ws.addEventListener('error', () => ws.close());
|
|
94
|
+
};
|
|
95
|
+
open();
|
|
96
|
+
return () => {
|
|
97
|
+
closed = true;
|
|
98
|
+
if (retry)
|
|
99
|
+
clearTimeout(retry);
|
|
100
|
+
socket?.close();
|
|
101
|
+
socket = null;
|
|
102
|
+
setConnected(false);
|
|
103
|
+
setRows([]);
|
|
104
|
+
};
|
|
105
|
+
}, [enabled, channel, roomId, event]);
|
|
106
|
+
const users = useMemo(() => presenceUsers(rows, selfId, resolveUser), [rows, selfId, resolveUser]);
|
|
107
|
+
return { users, connected };
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=usePresence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usePresence.js","sourceRoot":"","sources":["../../../src/react/chat/usePresence.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type CSSProperties, type ReactNode } from 'react';
|
|
1
|
+
import { type CSSProperties, type KeyboardEvent, type ReactNode } from 'react';
|
|
2
2
|
import { type ControlSize } from '../lib/field.js';
|
|
3
3
|
import { type StyleableProps } from '../styling.js';
|
|
4
4
|
export type SecretInputSlot = 'root' | 'input' | 'button';
|
|
@@ -52,6 +52,11 @@ export interface SecretInputProps extends StyleableProps<SecretInputSlot> {
|
|
|
52
52
|
'aria-invalid'?: boolean | 'true' | 'false';
|
|
53
53
|
'aria-describedby'?: string;
|
|
54
54
|
autoFocus?: boolean;
|
|
55
|
+
/** Disable typing while a form submits (the eye stays usable — seeing what you typed is
|
|
56
|
+
* harmless while it is in flight). */
|
|
57
|
+
disabled?: boolean;
|
|
58
|
+
/** Fires on Enter in THIS field — the login/rotation forms submit from their last field. */
|
|
59
|
+
onEnter?: (event: KeyboardEvent<HTMLInputElement>) => void;
|
|
55
60
|
className?: string;
|
|
56
61
|
style?: CSSProperties;
|
|
57
62
|
/** Container height on the shared control-height scale (`--control-h-*`, task
|
|
@@ -64,5 +69,5 @@ export interface SecretInputProps extends StyleableProps<SecretInputSlot> {
|
|
|
64
69
|
* otherwise. Reveal is uncontrolled by default; pass `reveal` to drive it from a
|
|
65
70
|
* parent (a "reveal all"). Tailwind-first, overridable per slot. No icon dependency.
|
|
66
71
|
*/
|
|
67
|
-
export declare const SecretInput: ({ value, onChange, reveal, onToggleReveal, label, placeholder, autoComplete, showToggle, showCopy, onCopied, onCopy, actions, id, name, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedBy, autoFocus, className, style, classNames, styles, unstyled, size, }: SecretInputProps) => import("react").JSX.Element;
|
|
72
|
+
export declare const SecretInput: ({ value, onChange, reveal, onToggleReveal, label, placeholder, autoComplete, showToggle, showCopy, onCopied, onCopy, actions, id, name, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedBy, autoFocus, disabled, onEnter, className, style, classNames, styles, unstyled, size, }: SecretInputProps) => import("react").JSX.Element;
|
|
68
73
|
//# sourceMappingURL=SecretInput.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SecretInput.d.ts","sourceRoot":"","sources":["../../../src/react/components/SecretInput.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,SAAS,EAAY,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"SecretInput.d.ts","sourceRoot":"","sources":["../../../src/react/components/SecretInput.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,aAAa,EAAE,KAAK,SAAS,EAAY,MAAM,OAAO,CAAC;AAEzF,OAAO,EAAwB,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACzE,OAAO,EAA8B,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAiEhF,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;AAE1D,MAAM,WAAW,gBAAiB,SAAQ,cAAc,CAAC,eAAe,CAAC;IACvE,KAAK,EAAE,MAAM,CAAC;IACd,wEAAwE;IACxE,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC;gEAC4D;IAC5D,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,oFAAoF;IACpF,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;IAC5B,4DAA4D;IAC5D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;kEAE8D;IAC9D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,2EAA2E;IAC3E,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;uEACmE;IACnE,cAAc,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC;IAC5C,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;2CACuC;IACvC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,4FAA4F;IAC5F,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,CAAC,gBAAgB,CAAC,KAAK,IAAI,CAAC;IAC3D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB;kCAC8B;IAC9B,IAAI,CAAC,EAAE,WAAW,CAAC;CACpB;AAED;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,iSA0BzB,gBAAgB,gCAmFlB,CAAC"}
|
|
@@ -19,7 +19,7 @@ const BTN_CLASS = `h-auto min-h-0 w-auto inline-flex shrink-0 items-center justi
|
|
|
19
19
|
* otherwise. Reveal is uncontrolled by default; pass `reveal` to drive it from a
|
|
20
20
|
* parent (a "reveal all"). Tailwind-first, overridable per slot. No icon dependency.
|
|
21
21
|
*/
|
|
22
|
-
export const SecretInput = ({ value, onChange, reveal, onToggleReveal, label = 'secret value', placeholder, autoComplete = 'off', showToggle = true, showCopy = true, onCopied, onCopy, actions, id, name, 'aria-invalid': ariaInvalid, 'aria-describedby': ariaDescribedBy, autoFocus = false, className, style, classNames, styles, unstyled, size = 'md', }) => {
|
|
22
|
+
export const SecretInput = ({ value, onChange, reveal, onToggleReveal, label = 'secret value', placeholder, autoComplete = 'off', showToggle = true, showCopy = true, onCopied, onCopy, actions, id, name, 'aria-invalid': ariaInvalid, 'aria-describedby': ariaDescribedBy, autoFocus = false, disabled, onEnter, className, style, classNames, styles, unstyled, size = 'md', }) => {
|
|
23
23
|
const [internalShown, setInternalShown] = useState(false);
|
|
24
24
|
const { copied: internalCopied, copy } = useCopyToClipboard();
|
|
25
25
|
// `onCopy` bypasses `useCopyToClipboard`, so its "copied ✓" tick has to be kept
|
|
@@ -46,7 +46,10 @@ export const SecretInput = ({ value, onChange, reveal, onToggleReveal, label = '
|
|
|
46
46
|
return (_jsxs("div", { className: resolveClass(`flex items-center gap-0.5 ${CONTROL_HEIGHT_CLASS[size]} ${TAP_TARGET_HEIGHT} rounded-field border border-field-border bg-field-bg px-1 transition focus-within:border-field-ring focus-within:ring-2 focus-within:ring-field-ring/30 has-[[aria-invalid=true]]:border-field-error`, classNames?.root ?? className, unstyled), style: resolveStyle({}, styles?.root ?? style, unstyled), children: [_jsx("input", { id: id, name: name,
|
|
47
47
|
// guardrails-ignore no-raw-inputs: this IS the SecretInput Field-family
|
|
48
48
|
// primitive — wraps the raw masked-text input it exposes.
|
|
49
|
-
type: shown ? 'text' : 'password', value: value, readOnly: readOnly, onChange: onChange ? (e) => onChange(e.target.value) : undefined,
|
|
49
|
+
type: shown ? 'text' : 'password', value: value, readOnly: readOnly, onChange: onChange ? (e) => onChange(e.target.value) : undefined, onKeyDown: onEnter ? (e) => {
|
|
50
|
+
if (e.key === 'Enter')
|
|
51
|
+
onEnter(e);
|
|
52
|
+
} : undefined, disabled: disabled, placeholder: placeholder, "aria-label": label, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedBy, autoComplete: autoComplete, spellCheck: false,
|
|
50
53
|
// biome-ignore lint/a11y/noAutofocus: opt-in via prop, used in editor rows
|
|
51
54
|
autoFocus: autoFocus, className: resolveClass('min-w-0 flex-1 self-stretch bg-transparent px-2 font-mono text-base text-field-fg outline-none placeholder:text-field-placeholder sm:text-sm', classNames?.input, unstyled) }), actions, showToggle && (_jsx(IconButton, { label: shown ? `Hide ${label}` : `Reveal ${label}`, onClick: toggle, className: resolveClass(BTN_CLASS, classNames?.button, unstyled), children: shown ? _jsx(EyeOff, {}) : _jsx(Eye, {}) })), showCopy && (_jsx(IconButton, { label: `Copy ${label}`, onClick: handleCopy, className: resolveClass(BTN_CLASS, classNames?.button, unstyled), children: copied ? _jsx(Check, {}) : _jsx(Copy, {}) }))] }));
|
|
52
55
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SecretInput.js","sourceRoot":"","sources":["../../../src/react/components/SecretInput.tsx"],"names":[],"mappings":";AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"SecretInput.js","sourceRoot":"","sources":["../../../src/react/components/SecretInput.tsx"],"names":[],"mappings":";AAAA,OAAO,EAA0D,QAAQ,EAAE,MAAM,OAAO,CAAC;AACzF,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAoB,MAAM,iBAAiB,CAAC;AACzE,OAAO,EAAE,YAAY,EAAE,YAAY,EAAuB,MAAM,eAAe,CAAC;AAChF,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAEpE,yEAAyE;AACzE,MAAM,GAAG,GAAG,GAAG,EAAE,CAAC,CAChB,eACE,OAAO,EAAC,WAAW,EACnB,KAAK,EAAC,IAAI,EACV,MAAM,EAAC,IAAI,EACX,IAAI,EAAC,MAAM,EACX,MAAM,EAAC,cAAc,EACrB,WAAW,EAAC,GAAG,iBACH,MAAM,aAElB,eAAM,CAAC,EAAC,kDAAkD,GAAG,EAC7D,iBAAQ,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,IAAI,EAAC,CAAC,EAAC,GAAG,GAAG,IAC5B,CACP,CAAC;AACF,MAAM,MAAM,GAAG,GAAG,EAAE,CAAC,CACnB,eACE,OAAO,EAAC,WAAW,EACnB,KAAK,EAAC,IAAI,EACV,MAAM,EAAC,IAAI,EACX,IAAI,EAAC,MAAM,EACX,MAAM,EAAC,cAAc,EACrB,WAAW,EAAC,GAAG,iBACH,MAAM,aAElB,eAAM,CAAC,EAAC,sIAAsI,GAAG,EACjJ,eAAM,CAAC,EAAC,YAAY,GAAG,IACnB,CACP,CAAC;AACF,MAAM,IAAI,GAAG,GAAG,EAAE,CAAC,CACjB,eACE,OAAO,EAAC,WAAW,EACnB,KAAK,EAAC,IAAI,EACV,MAAM,EAAC,IAAI,EACX,IAAI,EAAC,MAAM,EACX,MAAM,EAAC,cAAc,EACrB,WAAW,EAAC,GAAG,iBACH,MAAM,aAElB,eAAM,CAAC,EAAC,GAAG,EAAC,CAAC,EAAC,GAAG,EAAC,KAAK,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,EAAE,EAAC,GAAG,GAAG,EAClD,eAAM,CAAC,EAAC,yDAAyD,GAAG,IAChE,CACP,CAAC;AACF,MAAM,KAAK,GAAG,GAAG,EAAE,CAAC,CAClB,cACE,OAAO,EAAC,WAAW,EACnB,KAAK,EAAC,IAAI,EACV,MAAM,EAAC,IAAI,EACX,IAAI,EAAC,MAAM,EACX,MAAM,EAAC,cAAc,EACrB,WAAW,EAAC,GAAG,iBACH,MAAM,YAElB,eAAM,CAAC,EAAC,iBAAiB,GAAG,GACxB,CACP,CAAC;AAEF,iFAAiF;AACjF,qFAAqF;AACrF,MAAM,SAAS,GAAG,4JAA4J,UAAU,EAAE,CAAC;AAkE3L;;;;;GAKG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,EAC1B,KAAK,EACL,QAAQ,EACR,MAAM,EACN,cAAc,EACd,KAAK,GAAG,cAAc,EACtB,WAAW,EACX,YAAY,GAAG,KAAK,EACpB,UAAU,GAAG,IAAI,EACjB,QAAQ,GAAG,IAAI,EACf,QAAQ,EACR,MAAM,EACN,OAAO,EACP,EAAE,EACF,IAAI,EACJ,cAAc,EAAE,WAAW,EAC3B,kBAAkB,EAAE,eAAe,EACnC,SAAS,GAAG,KAAK,EACjB,QAAQ,EACR,OAAO,EACP,SAAS,EACT,KAAK,EACL,UAAU,EACV,MAAM,EACN,QAAQ,EACR,IAAI,GAAG,IAAI,GACM,EAAE,EAAE;IACrB,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC1D,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,IAAI,EAAE,GAAG,kBAAkB,EAAE,CAAC;IAC9D,gFAAgF;IAChF,6EAA6E;IAC7E,MAAM,CAAC,cAAc,EAAE,iBAAiB,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC5D,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,cAAc,CAAC;IACxD,MAAM,KAAK,GAAG,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC;IAC5D,MAAM,QAAQ,GAAG,CAAC,QAAQ,CAAC;IAE3B,MAAM,MAAM,GAAG,GAAG,EAAE,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAEvF,MAAM,UAAU,GAAG,GAAG,EAAE;QACtB,IAAI,MAAM,EAAE,CAAC;YACX,KAAK,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE;gBAC5C,QAAQ,EAAE,CAAC,KAAK,CAAC,CAAC;gBAClB,iBAAiB,CAAC,IAAI,CAAC,CAAC;gBACxB,UAAU,CAAC,GAAG,EAAE,CAAC,iBAAiB,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,CAAC;YACnD,CAAC,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QACD,KAAK,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE;YAC3B,IAAI,EAAE;gBAAE,QAAQ,EAAE,CAAC,KAAK,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,OAAO,CACL,eACE,SAAS,EAAE,YAAY,CACrB,6BAA6B,oBAAoB,CAAC,IAAI,CAAC,IAAI,iBAAiB,uMAAuM,EACnR,UAAU,EAAE,IAAI,IAAI,SAAS,EAC7B,QAAQ,CACT,EACD,KAAK,EAAE,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,IAAI,KAAK,EAAE,QAAQ,CAAC,aAExD,gBACE,EAAE,EAAE,EAAE,EACN,IAAI,EAAE,IAAI;gBACV,wEAAwE;gBACxE,0DAA0D;gBAC1D,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,EACjC,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,EAChE,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE;oBACzB,IAAI,CAAC,CAAC,GAAG,KAAK,OAAO;wBAAE,OAAO,CAAC,CAAC,CAAC,CAAC;gBACpC,CAAC,CAAC,CAAC,CAAC,SAAS,EACb,QAAQ,EAAE,QAAQ,EAClB,WAAW,EAAE,WAAW,gBACZ,KAAK,kBACH,WAAW,sBACP,eAAe,EACjC,YAAY,EAAE,YAAY,EAC1B,UAAU,EAAE,KAAK;gBACjB,2EAA2E;gBAC3E,SAAS,EAAE,SAAS,EACpB,SAAS,EAAE,YAAY,CACrB,8IAA8I,EAC9I,UAAU,EAAE,KAAK,EACjB,QAAQ,CACT,GACD,EACD,OAAO,EACP,UAAU,IAAI,CACb,KAAC,UAAU,IACT,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,QAAQ,KAAK,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,EAAE,EAClD,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,YAAY,CAAC,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,CAAC,YAE/D,KAAK,CAAC,CAAC,CAAC,KAAC,MAAM,KAAG,CAAC,CAAC,CAAC,KAAC,GAAG,KAAG,GAClB,CACd,EACA,QAAQ,IAAI,CACX,KAAC,UAAU,IACT,KAAK,EAAE,QAAQ,KAAK,EAAE,EACtB,OAAO,EAAE,UAAU,EACnB,SAAS,EAAE,YAAY,CAAC,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,CAAC,YAE/D,MAAM,CAAC,CAAC,CAAC,KAAC,KAAK,KAAG,CAAC,CAAC,CAAC,KAAC,IAAI,KAAG,GACnB,CACd,IACG,CACP,CAAC;AACJ,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TooltipImpl.d.ts","sourceRoot":"","sources":["../../../src/react/components/TooltipImpl.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE7D;;;;;;GAMG;AACH,UAAU,gBAAiB,SAAQ,YAAY;IAC7C;;;qCAGiC;IACjC,GAAG,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;CACzB;AAED;;;;GAIG;AACH,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,EAClC,QAAQ,EACR,OAAO,EACP,IAAI,EACJ,SAAS,EACT,SAAS,EACT,QAAQ,EACR,MAAc,EACd,UAAU,EACV,aAAa,EACb,SAAS,EACT,GAAG,GACJ,EAAE,gBAAgB,+
|
|
1
|
+
{"version":3,"file":"TooltipImpl.d.ts","sourceRoot":"","sources":["../../../src/react/components/TooltipImpl.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE7D;;;;;;GAMG;AACH,UAAU,gBAAiB,SAAQ,YAAY;IAC7C;;;qCAGiC;IACjC,GAAG,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;CACzB;AAED;;;;GAIG;AACH,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,EAClC,QAAQ,EACR,OAAO,EACP,IAAI,EACJ,SAAS,EACT,SAAS,EACT,QAAQ,EACR,MAAc,EACd,UAAU,EACV,aAAa,EACb,SAAS,EACT,GAAG,GACJ,EAAE,gBAAgB,+BAyVlB"}
|
|
@@ -16,6 +16,10 @@ export default function TooltipImpl({ children, content, side, placement, multil
|
|
|
16
16
|
const resolvedSide = placement ?? side ?? 'top';
|
|
17
17
|
const triggerRef = useRef(null);
|
|
18
18
|
const wasFocusedRef = useRef(false);
|
|
19
|
+
/** The node a TOUCH disclosure's taps land on — the ⓘ, or the `tap` branch's span. */
|
|
20
|
+
const touchTriggerRef = useRef(null);
|
|
21
|
+
/** True while one of that trigger's own events is being dispatched — see `onTouchOpenChange`. */
|
|
22
|
+
const inTriggerEventRef = useRef(false);
|
|
19
23
|
// Replay the arming gesture. Radix opens on `pointermove` over the trigger and on
|
|
20
24
|
// focus; both already happened before this module existed, so re-dispatch one on
|
|
21
25
|
// the real trigger node. `focusin`/`pointermove` both bubble, which is what React's
|
|
@@ -173,7 +177,16 @@ export default function TooltipImpl({ children, content, side, placement, multil
|
|
|
173
177
|
};
|
|
174
178
|
// `open` is deliberately absent: see `shown` above.
|
|
175
179
|
}, [disabled]);
|
|
176
|
-
const bubble = (_jsx(RTooltip.Portal, { children: _jsxs(RTooltip.Content, { "data-cb-tooltip": '', side: resolvedSide, sideOffset: 6, collisionPadding: FLOATING_COLLISION_PADDING, style: maxWidth != null ? { maxWidth } : undefined, onPointerDownOutside: () =>
|
|
180
|
+
const bubble = (_jsx(RTooltip.Portal, { children: _jsxs(RTooltip.Content, { "data-cb-tooltip": '', side: resolvedSide, sideOffset: 6, collisionPadding: FLOATING_COLLISION_PADDING, style: maxWidth != null ? { maxWidth } : undefined, onPointerDownOutside: (e) => {
|
|
181
|
+
// A touch disclosure's OWN trigger is outside the bubble, but a tap on it is the
|
|
182
|
+
// toggle's business (it closes an open bubble on its click). Letting the layer
|
|
183
|
+
// dismiss here as well would close on this pointerdown and re-open on the click.
|
|
184
|
+
if (!hasHover && touchTriggerRef.current?.contains(e.target)) {
|
|
185
|
+
e.preventDefault();
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
setOpen(false);
|
|
189
|
+
}, className: cn(
|
|
177
190
|
// NOTE: the bubble is a transient hint and must NEVER be a click target —
|
|
178
191
|
// a focus-opened tooltip (e.g. a prompt auto-focusing its safe default
|
|
179
192
|
// button) would otherwise hover over the adjacent button and eat the
|
|
@@ -201,10 +214,46 @@ export default function TooltipImpl({ children, content, side, placement, multil
|
|
|
201
214
|
if (mobile === 'off')
|
|
202
215
|
return _jsx(_Fragment, { children: children });
|
|
203
216
|
const toggle = () => setOpen((o) => !o);
|
|
217
|
+
/*
|
|
218
|
+
* 🔴 On touch, the TAP owns `open`, and Radix's trigger handlers are told nothing they say.
|
|
219
|
+
*
|
|
220
|
+
* Tasks 2082 + 2093, measured in a Pixel 7 context on station's Console: the first tap on
|
|
221
|
+
* every ⓘ did nothing and the second opened it. A real tap fires `pointerdown → pointerup →
|
|
222
|
+
* focus → click`. Radix's Trigger clears its pointer-down flag on `pointerup`, so the
|
|
223
|
+
* `focus` that follows reads as KEYBOARD focus and opens; then this branch's click `toggle`
|
|
224
|
+
* flips that fresh `true` back to `false`. (Radix also composes `onClose` onto every click
|
|
225
|
+
* and every `pointerdown` on an open trigger, which is the half the tasks first suspected.)
|
|
226
|
+
*
|
|
227
|
+
* So while one of the trigger's own events is being dispatched, `onOpenChange` from Radix
|
|
228
|
+
* is ignored — the flag is set by the handlers below, which `composeEventHandlers` runs
|
|
229
|
+
* BEFORE Radix's, and cleared in a microtask, which runs once the whole dispatch returns.
|
|
230
|
+
* Nothing is `preventDefault`ed, so a `mobile="tap"` trigger that is a link still
|
|
231
|
+
* navigates. Every OTHER close Radix makes still lands: Escape, scroll, and another
|
|
232
|
+
* tooltip opening. `e2e/tooltip-tap.spec.ts` is the proof, in the `touch` project — it
|
|
233
|
+
* cannot be a happy-dom case, because happy-dom fires whatever order a spec writes.
|
|
234
|
+
*/
|
|
235
|
+
const markTriggerEvent = () => {
|
|
236
|
+
inTriggerEventRef.current = true;
|
|
237
|
+
queueMicrotask(() => {
|
|
238
|
+
inTriggerEventRef.current = false;
|
|
239
|
+
});
|
|
240
|
+
};
|
|
241
|
+
const onTouchOpenChange = (next) => {
|
|
242
|
+
if (inTriggerEventRef.current)
|
|
243
|
+
return;
|
|
244
|
+
setOpen(next);
|
|
245
|
+
};
|
|
246
|
+
const touchTriggerProps = {
|
|
247
|
+
ref: touchTriggerRef,
|
|
248
|
+
onPointerDown: markTriggerEvent,
|
|
249
|
+
onFocus: markTriggerEvent,
|
|
250
|
+
onBlur: markTriggerEvent,
|
|
251
|
+
onClick: markTriggerEvent,
|
|
252
|
+
};
|
|
204
253
|
// `icon`: render the trigger as-is plus a tappable ⓘ disclosure that owns the
|
|
205
254
|
// tooltip (so a tap on the trigger still does its own action).
|
|
206
255
|
if (mobile === 'icon') {
|
|
207
|
-
return (_jsxs("span", { className: 'inline-flex items-center gap-1', children: [children, _jsx(RTooltip.Provider, { delayDuration: HOVER_HINT_INSTANT_MS, children: _jsxs(RTooltip.Root, { open: open, onOpenChange:
|
|
256
|
+
return (_jsxs("span", { className: 'inline-flex items-center gap-1', children: [children, _jsx(RTooltip.Provider, { delayDuration: HOVER_HINT_INSTANT_MS, children: _jsxs(RTooltip.Root, { open: open, onOpenChange: onTouchOpenChange, children: [_jsx(RTooltip.Trigger, { asChild: true, ...touchTriggerProps, children: _jsx("button", {
|
|
208
257
|
// guardrails-ignore no-raw-action-button: this IS the Tooltip primitive's
|
|
209
258
|
// own mobile disclosure trigger — Button/IconButton wrap every button in a
|
|
210
259
|
// Tooltip, so using one here would be circular.
|
|
@@ -217,6 +266,6 @@ export default function TooltipImpl({ children, content, side, placement, multil
|
|
|
217
266
|
}
|
|
218
267
|
// `tap`: tapping the trigger itself toggles the tooltip. Instant for the same reason
|
|
219
268
|
// as the `icon` branch above — the tap IS the request.
|
|
220
|
-
return (_jsx(RTooltip.Provider, { delayDuration: HOVER_HINT_INSTANT_MS, children: _jsxs(RTooltip.Root, { open: open, onOpenChange:
|
|
269
|
+
return (_jsx(RTooltip.Provider, { delayDuration: HOVER_HINT_INSTANT_MS, children: _jsxs(RTooltip.Root, { open: open, onOpenChange: onTouchOpenChange, children: [_jsx(RTooltip.Trigger, { asChild: true, ...touchTriggerProps, children: _jsx("span", { onClick: toggle, children: children }) }), bubble] }) }));
|
|
221
270
|
}
|
|
222
271
|
//# sourceMappingURL=TooltipImpl.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TooltipImpl.js","sourceRoot":"","sources":["../../../src/react/components/TooltipImpl.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,QAAQ,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAClF,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAClC,OAAO,EAAE,0BAA0B,EAAE,MAAM,0BAA0B,CAAC;AACtE,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAkBnF;;;;GAIG;AACH,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,EAClC,QAAQ,EACR,OAAO,EACP,IAAI,EACJ,SAAS,EACT,SAAS,EACT,QAAQ,EACR,MAAM,GAAG,KAAK,EACd,UAAU,EACV,aAAa,EACb,SAAS,EACT,GAAG,GACc;IACjB,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IAC/B,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IACxC,MAAM,YAAY,GAAG,SAAS,IAAI,IAAI,IAAI,KAAK,CAAC;IAChD,MAAM,UAAU,GAAG,MAAM,CAAqB,IAAI,CAAC,CAAC;IACpD,MAAM,aAAa,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"TooltipImpl.js","sourceRoot":"","sources":["../../../src/react/components/TooltipImpl.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EmojiPicker.d.ts","sourceRoot":"","sources":["../../../src/react/emoji/EmojiPicker.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"EmojiPicker.d.ts","sourceRoot":"","sources":["../../../src/react/emoji/EmojiPicker.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAwC,MAAM,OAAO,CAAC;AAuE7E,MAAM,WAAW,gBAAgB;IAC/B,8EAA8E;IAC9E,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,iEAAiE;IACjE,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,gEAAgE;IAChE,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IAC5C,gFAAgF;IAChF,GAAG,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,UAAU,CAAC;IAC7D;8EAC0E;IAC1E,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,mFAAmF;IACnF,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,gDAAgD;IAChD,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;CACxC;AAuCD;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,EAC1B,QAAQ,EACR,OAAO,EACP,KAAc,EACd,MAAa,EACb,eAAwB,EACxB,GAAc,EACd,MAAc,EACd,OAAO,EACP,IAAI,EACJ,YAAY,GACb,EAAE,gBAAgB,+BA4BlB"}
|
|
@@ -1,18 +1,59 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
3
3
|
import { IconButton } from '../components/IconButton.js';
|
|
4
4
|
import { Popover } from '../components/Popover.js';
|
|
5
5
|
import { Skeleton } from '../components/Skeleton.js';
|
|
6
6
|
import { resolveEmojiTheme } from './emojiSearch.js';
|
|
7
|
-
// The emoji-mart
|
|
8
|
-
//
|
|
9
|
-
// async `data` loader (never statically imported — that would tax every page).
|
|
10
|
-
const Picker = lazy(() => import('@emoji-mart/react'));
|
|
7
|
+
// The emoji-mart engine and its ~300 KB data are BOTH lazy: nothing loads until the panel
|
|
8
|
+
// first mounts, and the data is fetched through emoji-mart's async `data` loader.
|
|
11
9
|
const loadEmojiData = () => import('@emoji-mart/data').then((m) => m.default);
|
|
12
|
-
/**
|
|
10
|
+
/**
|
|
11
|
+
* 🔴 emoji-mart's `Picker` custom element, mounted directly — NOT through `@emoji-mart/react`
|
|
12
|
+
* (cursedbelt 5.0.1). That wrapper is twenty lines (`new Picker({ ...props, ref })` into a div),
|
|
13
|
+
* and depending on it cost every consumer something either way: as an optional peer it made
|
|
14
|
+
* `npm install cursedbelt` fail ERESOLVE (it peers `react <=18`; task 2087), and as `unpeered`
|
|
15
|
+
* (5.0.0) a literal `import('@emoji-mart/react')` broke every clean-install `vite build` that
|
|
16
|
+
* imports `cursedbelt/react`, because Vite stubs a missing import only for a DECLARED optional
|
|
17
|
+
* peer. `emoji-mart` itself IS a declared optional peer, so a consumer that never renders this
|
|
18
|
+
* builds green with it absent, and one that does gets a named error rather than a blank panel.
|
|
19
|
+
*/
|
|
20
|
+
function EmojiMartPanel(props) {
|
|
21
|
+
const host = useRef(null);
|
|
22
|
+
const picker = useRef(null);
|
|
23
|
+
const latest = useRef(props);
|
|
24
|
+
latest.current = props;
|
|
25
|
+
const [ready, setReady] = useState(false);
|
|
26
|
+
const [failed, setFailed] = useState(null);
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
let live = true;
|
|
29
|
+
import('emoji-mart').then(({ Picker }) => {
|
|
30
|
+
if (!live || !host.current)
|
|
31
|
+
return;
|
|
32
|
+
picker.current = new Picker({ ...latest.current, ref: host });
|
|
33
|
+
setReady(true);
|
|
34
|
+
}, (cause) => {
|
|
35
|
+
if (live) {
|
|
36
|
+
setFailed(new Error("cursedbelt: EmojiPicker needs the optional peer 'emoji-mart' (and '@emoji-mart/data') — run `bun add emoji-mart @emoji-mart/data` in the app that renders it.", { cause }));
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
return () => {
|
|
40
|
+
live = false;
|
|
41
|
+
picker.current = null;
|
|
42
|
+
host.current?.replaceChildren();
|
|
43
|
+
};
|
|
44
|
+
}, []);
|
|
45
|
+
// The wrapper's contract: every render pushes the current props into the live element.
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
picker.current?.update?.(props);
|
|
48
|
+
});
|
|
49
|
+
if (failed)
|
|
50
|
+
throw failed;
|
|
51
|
+
return (_jsxs(_Fragment, { children: [!ready && _jsx(Skeleton, { className: 'h-[420px] w-[352px]' }), _jsx("div", { ref: host })] }));
|
|
52
|
+
}
|
|
53
|
+
/** The emoji-mart grid itself, themed + lazy, behind a skeleton until the engine has loaded. */
|
|
13
54
|
function EmojiPanel({ onSelect, theme = 'auto', locale = 'en', previewPosition = 'none', set = 'native', }) {
|
|
14
55
|
const resolvedTheme = useMemo(() => resolveEmojiTheme(theme), [theme]);
|
|
15
|
-
return (_jsx(
|
|
56
|
+
return (_jsx(EmojiMartPanel, { data: loadEmojiData, onEmojiSelect: (e) => onSelect(e.native), theme: resolvedTheme, locale: locale, previewPosition: previewPosition, set: set, navPosition: 'top', skinTonePosition: 'search', dynamicWidth: false }));
|
|
16
57
|
}
|
|
17
58
|
/** A neutral default trigger when none is supplied. */
|
|
18
59
|
function DefaultTrigger() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EmojiPicker.js","sourceRoot":"","sources":["../../../src/react/emoji/EmojiPicker.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"EmojiPicker.js","sourceRoot":"","sources":["../../../src/react/emoji/EmojiPicker.tsx"],"names":[],"mappings":"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"}
|
package/dist/react/index.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export { MessageScroller, type MessageScrollerProps } from './chat/MessageScroll
|
|
|
13
13
|
export { MessageThread, type MessageThreadProps } from './chat/MessageThread.js';
|
|
14
14
|
export { PresenceRoster, type PresenceRosterProps } from './chat/PresenceRoster.js';
|
|
15
15
|
export { StatusMarker, type StatusMarkerProps } from './chat/StatusMarker.js';
|
|
16
|
+
export { PRESENCE_RETRY_MS, type PresenceEntryLike, type PresenceSubscription, presenceUsers, type UsePresenceOptions, usePresence, } from './chat/usePresence.js';
|
|
16
17
|
export { Markdown, type MarkdownProps } from './markdown/Markdown.js';
|
|
17
18
|
export type { ChatAttachment, ChatAuthor, ChatMessage, MessageStatus, PresenceUser, } from './chat/types.js';
|
|
18
19
|
export { PageBottom, usePageBottomSpace } from './compat.js';
|