@augmenting-integrations/auth 8.14.0 → 8.14.2

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.
@@ -23,29 +23,68 @@ __export(UserMenu_exports, {
23
23
  });
24
24
  module.exports = __toCommonJS(UserMenu_exports);
25
25
  var import_jsx_runtime = require("react/jsx-runtime");
26
+ var import_lucide_react = require("lucide-react");
27
+ var import_radix_ui = require("radix-ui");
28
+ var import_react = require("next-auth/react");
26
29
  var import_AppUserProvider = require("./AppUserProvider.js");
27
- var import_SignOutButton = require("./SignOutButton.js");
30
+ function initialsFor(nameOrEmail, email) {
31
+ const source = (nameOrEmail || email || "?").trim();
32
+ if (!source) return "?";
33
+ if (source.includes("@")) {
34
+ const local = source.split("@")[0] ?? "";
35
+ return local.slice(0, 2).toUpperCase() || "?";
36
+ }
37
+ const parts = source.split(/\s+/).filter(Boolean);
38
+ if (parts.length === 0) return "?";
39
+ if (parts.length === 1) return (parts[0]?.slice(0, 2) ?? "?").toUpperCase();
40
+ return ((parts[0]?.[0] ?? "") + (parts[1]?.[0] ?? "")).toUpperCase();
41
+ }
28
42
  function UserMenu({
29
43
  signOutCallbackUrl = "/",
30
44
  className
31
45
  }) {
32
46
  const state = (0, import_AppUserProvider.useAppUser)();
33
47
  if (state.kind === "anonymous") return null;
34
- const email = state.kind === "db" ? state.user.email : state.user.email;
35
- const name = state.kind === "db" ? state.user.name : state.user.name ?? state.user.email;
36
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
37
- "div",
38
- {
39
- className: ["hidden items-center gap-2 md:flex", className].filter(Boolean).join(" "),
40
- children: [
41
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "text-right text-xs leading-tight", children: [
42
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "font-medium text-foreground", children: name ?? email }),
43
- email && name && name !== email ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-muted-foreground", children: email }) : null
44
- ] }),
45
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_SignOutButton.SignOutButton, { callbackUrl: signOutCallbackUrl })
46
- ]
47
- }
48
- );
48
+ const email = state.user.email;
49
+ const name = state.kind === "db" ? state.user.name : state.user.name ?? null;
50
+ const display = name && name !== email ? name : email;
51
+ const initials = initialsFor(name ?? null, email);
52
+ const triggerClass = [
53
+ "inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-full border border-border bg-muted text-sm font-semibold text-foreground transition hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
54
+ className
55
+ ].filter(Boolean).join(" ");
56
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_radix_ui.DropdownMenu.Root, { children: [
57
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_radix_ui.DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", "aria-label": "User menu", className: triggerClass, children: initials }) }),
58
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_radix_ui.DropdownMenu.Portal, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
59
+ import_radix_ui.DropdownMenu.Content,
60
+ {
61
+ align: "end",
62
+ sideOffset: 6,
63
+ className: "z-[100] min-w-56 overflow-hidden rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0",
64
+ children: [
65
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "px-2 py-2", children: [
66
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "truncate text-sm font-medium text-foreground", children: display }),
67
+ name && email && name !== email ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "truncate text-xs text-muted-foreground", children: email }) : null
68
+ ] }),
69
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_radix_ui.DropdownMenu.Separator, { className: "-mx-1 my-1 h-px bg-border" }),
70
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
71
+ import_radix_ui.DropdownMenu.Item,
72
+ {
73
+ onSelect: (e) => {
74
+ e.preventDefault();
75
+ void (0, import_react.signOut)({ callbackUrl: signOutCallbackUrl });
76
+ },
77
+ className: "flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm text-foreground outline-none focus:bg-accent focus:text-accent-foreground",
78
+ children: [
79
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.LogOut, { className: "h-4 w-4" }),
80
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Sign out" })
81
+ ]
82
+ }
83
+ )
84
+ ]
85
+ }
86
+ ) })
87
+ ] });
49
88
  }
50
89
  // Annotate the CommonJS export names for ESM import in node:
51
90
  0 && (module.exports = {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/client/UserMenu.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { useAppUser } from \"./AppUserProvider.js\";\nimport { SignOutButton } from \"./SignOutButton.js\";\n\n// =============================================================================\n// UserMenu -- header chrome showing the current user's identity + sign-out.\n//\n// Renders nothing for anonymous state. For db / session state shows the\n// user's name (or email fallback) and a sign-out button. Keeps the layout\n// simple (no dropdown) so it works inside the AppShell header without extra\n// portal-positioning. Spokes that want a richer dropdown can compose\n// <SignOutButton> themselves.\n// =============================================================================\n\nexport function UserMenu({\n signOutCallbackUrl = \"/\",\n className,\n}: {\n signOutCallbackUrl?: string;\n className?: string;\n}) {\n const state = useAppUser();\n if (state.kind === \"anonymous\") return null;\n\n const email = state.kind === \"db\" ? state.user.email : state.user.email;\n const name =\n state.kind === \"db\" ? state.user.name : (state.user.name ?? state.user.email);\n\n return (\n <div\n className={[\"hidden items-center gap-2 md:flex\", className]\n .filter(Boolean)\n .join(\" \")}\n >\n <div className=\"text-right text-xs leading-tight\">\n <p className=\"font-medium text-foreground\">{name ?? email}</p>\n {email && name && name !== email ? (\n <p className=\"text-muted-foreground\">{email}</p>\n ) : null}\n </div>\n <SignOutButton callbackUrl={signOutCallbackUrl} />\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAoCM;AAjCN,6BAA2B;AAC3B,2BAA8B;AAYvB,SAAS,SAAS;AAAA,EACvB,qBAAqB;AAAA,EACrB;AACF,GAGG;AACD,QAAM,YAAQ,mCAAW;AACzB,MAAI,MAAM,SAAS,YAAa,QAAO;AAEvC,QAAM,QAAQ,MAAM,SAAS,OAAO,MAAM,KAAK,QAAQ,MAAM,KAAK;AAClE,QAAM,OACJ,MAAM,SAAS,OAAO,MAAM,KAAK,OAAQ,MAAM,KAAK,QAAQ,MAAM,KAAK;AAEzE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,CAAC,qCAAqC,SAAS,EACvD,OAAO,OAAO,EACd,KAAK,GAAG;AAAA,MAEX;AAAA,qDAAC,SAAI,WAAU,oCACb;AAAA,sDAAC,OAAE,WAAU,+BAA+B,kBAAQ,OAAM;AAAA,UACzD,SAAS,QAAQ,SAAS,QACzB,4CAAC,OAAE,WAAU,yBAAyB,iBAAM,IAC1C;AAAA,WACN;AAAA,QACA,4CAAC,sCAAc,aAAa,oBAAoB;AAAA;AAAA;AAAA,EAClD;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../src/client/UserMenu.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { LogOut } from \"lucide-react\";\nimport { DropdownMenu as DM } from \"radix-ui\";\nimport { signOut } from \"next-auth/react\";\nimport { useAppUser } from \"./AppUserProvider.js\";\n\n// =============================================================================\n// UserMenu -- compact identity + sign-out surface for the AppShell header.\n//\n// Renders as an avatar/initials button (h-9 w-9) that opens a Radix\n// DropdownMenu containing the user's display name, email, and a Sign out\n// item. Keyboard a11y is handled by Radix: Tab to the trigger, Enter/Space\n// to open, ArrowUp/Down between items, Enter to activate, Esc to close\n// and restore focus to the trigger.\n//\n// This component used to render name + email + Sign-out inline. That\n// worked inside the old 264px sidebar footer but overflowed the new\n// horizontal header. The current shape consumes a fixed 36x36 slot\n// regardless of name/email length.\n// =============================================================================\n\nfunction initialsFor(nameOrEmail: string | null | undefined, email: string): string {\n const source = (nameOrEmail || email || \"?\").trim();\n if (!source) return \"?\";\n if (source.includes(\"@\")) {\n const local = source.split(\"@\")[0] ?? \"\";\n return local.slice(0, 2).toUpperCase() || \"?\";\n }\n const parts = source.split(/\\s+/).filter(Boolean);\n if (parts.length === 0) return \"?\";\n if (parts.length === 1) return (parts[0]?.slice(0, 2) ?? \"?\").toUpperCase();\n return ((parts[0]?.[0] ?? \"\") + (parts[1]?.[0] ?? \"\")).toUpperCase();\n}\n\nexport function UserMenu({\n signOutCallbackUrl = \"/\",\n className,\n}: {\n signOutCallbackUrl?: string;\n className?: string;\n}) {\n const state = useAppUser();\n if (state.kind === \"anonymous\") return null;\n\n const email = state.user.email;\n const name = state.kind === \"db\" ? state.user.name : (state.user.name ?? null);\n const display = name && name !== email ? name : email;\n const initials = initialsFor(name ?? null, email);\n\n const triggerClass = [\n \"inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-full border border-border bg-muted text-sm font-semibold text-foreground transition hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n className,\n ]\n .filter(Boolean)\n .join(\" \");\n\n return (\n <DM.Root>\n <DM.Trigger asChild>\n <button type=\"button\" aria-label=\"User menu\" className={triggerClass}>\n {initials}\n </button>\n </DM.Trigger>\n <DM.Portal>\n <DM.Content\n align=\"end\"\n sideOffset={6}\n className=\"z-[100] min-w-56 overflow-hidden rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0\"\n >\n <div className=\"px-2 py-2\">\n <p className=\"truncate text-sm font-medium text-foreground\">{display}</p>\n {name && email && name !== email ? (\n <p className=\"truncate text-xs text-muted-foreground\">{email}</p>\n ) : null}\n </div>\n <DM.Separator className=\"-mx-1 my-1 h-px bg-border\" />\n <DM.Item\n onSelect={(e) => {\n e.preventDefault();\n void signOut({ callbackUrl: signOutCallbackUrl });\n }}\n className=\"flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm text-foreground outline-none focus:bg-accent focus:text-accent-foreground\"\n >\n <LogOut className=\"h-4 w-4\" />\n <span>Sign out</span>\n </DM.Item>\n </DM.Content>\n </DM.Portal>\n </DM.Root>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AA6DQ;AA1DR,0BAAuB;AACvB,sBAAmC;AACnC,mBAAwB;AACxB,6BAA2B;AAiB3B,SAAS,YAAY,aAAwC,OAAuB;AAClF,QAAM,UAAU,eAAe,SAAS,KAAK,KAAK;AAClD,MAAI,CAAC,OAAQ,QAAO;AACpB,MAAI,OAAO,SAAS,GAAG,GAAG;AACxB,UAAM,QAAQ,OAAO,MAAM,GAAG,EAAE,CAAC,KAAK;AACtC,WAAO,MAAM,MAAM,GAAG,CAAC,EAAE,YAAY,KAAK;AAAA,EAC5C;AACA,QAAM,QAAQ,OAAO,MAAM,KAAK,EAAE,OAAO,OAAO;AAChD,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,MAAI,MAAM,WAAW,EAAG,SAAQ,MAAM,CAAC,GAAG,MAAM,GAAG,CAAC,KAAK,KAAK,YAAY;AAC1E,WAAS,MAAM,CAAC,IAAI,CAAC,KAAK,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,KAAK,YAAY;AACrE;AAEO,SAAS,SAAS;AAAA,EACvB,qBAAqB;AAAA,EACrB;AACF,GAGG;AACD,QAAM,YAAQ,mCAAW;AACzB,MAAI,MAAM,SAAS,YAAa,QAAO;AAEvC,QAAM,QAAQ,MAAM,KAAK;AACzB,QAAM,OAAO,MAAM,SAAS,OAAO,MAAM,KAAK,OAAQ,MAAM,KAAK,QAAQ;AACzE,QAAM,UAAU,QAAQ,SAAS,QAAQ,OAAO;AAChD,QAAM,WAAW,YAAY,QAAQ,MAAM,KAAK;AAEhD,QAAM,eAAe;AAAA,IACnB;AAAA,IACA;AAAA,EACF,EACG,OAAO,OAAO,EACd,KAAK,GAAG;AAEX,SACE,6CAAC,gBAAAA,aAAG,MAAH,EACC;AAAA,gDAAC,gBAAAA,aAAG,SAAH,EAAW,SAAO,MACjB,sDAAC,YAAO,MAAK,UAAS,cAAW,aAAY,WAAW,cACrD,oBACH,GACF;AAAA,IACA,4CAAC,gBAAAA,aAAG,QAAH,EACC;AAAA,MAAC,gBAAAA,aAAG;AAAA,MAAH;AAAA,QACC,OAAM;AAAA,QACN,YAAY;AAAA,QACZ,WAAU;AAAA,QAEV;AAAA,uDAAC,SAAI,WAAU,aACb;AAAA,wDAAC,OAAE,WAAU,gDAAgD,mBAAQ;AAAA,YACpE,QAAQ,SAAS,SAAS,QACzB,4CAAC,OAAE,WAAU,0CAA0C,iBAAM,IAC3D;AAAA,aACN;AAAA,UACA,4CAAC,gBAAAA,aAAG,WAAH,EAAa,WAAU,6BAA4B;AAAA,UACpD;AAAA,YAAC,gBAAAA,aAAG;AAAA,YAAH;AAAA,cACC,UAAU,CAAC,MAAM;AACf,kBAAE,eAAe;AACjB,yBAAK,sBAAQ,EAAE,aAAa,mBAAmB,CAAC;AAAA,cAClD;AAAA,cACA,WAAU;AAAA,cAEV;AAAA,4DAAC,8BAAO,WAAU,WAAU;AAAA,gBAC5B,4CAAC,UAAK,sBAAQ;AAAA;AAAA;AAAA,UAChB;AAAA;AAAA;AAAA,IACF,GACF;AAAA,KACF;AAEJ;","names":["DM"]}
@@ -1 +1 @@
1
- {"version":3,"file":"UserMenu.d.ts","sourceRoot":"","sources":["../../src/client/UserMenu.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAc/B,wBAAgB,QAAQ,CAAC,EACvB,kBAAwB,EACxB,SAAS,GACV,EAAE;IACD,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,4BAuBA"}
1
+ {"version":3,"file":"UserMenu.d.ts","sourceRoot":"","sources":["../../src/client/UserMenu.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAkC/B,wBAAgB,QAAQ,CAAC,EACvB,kBAAwB,EACxB,SAAS,GACV,EAAE;IACD,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,4BAkDA"}
@@ -1,28 +1,67 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { LogOut } from "lucide-react";
4
+ import { DropdownMenu as DM } from "radix-ui";
5
+ import { signOut } from "next-auth/react";
3
6
  import { useAppUser } from "./AppUserProvider.js";
4
- import { SignOutButton } from "./SignOutButton.js";
7
+ function initialsFor(nameOrEmail, email) {
8
+ const source = (nameOrEmail || email || "?").trim();
9
+ if (!source) return "?";
10
+ if (source.includes("@")) {
11
+ const local = source.split("@")[0] ?? "";
12
+ return local.slice(0, 2).toUpperCase() || "?";
13
+ }
14
+ const parts = source.split(/\s+/).filter(Boolean);
15
+ if (parts.length === 0) return "?";
16
+ if (parts.length === 1) return (parts[0]?.slice(0, 2) ?? "?").toUpperCase();
17
+ return ((parts[0]?.[0] ?? "") + (parts[1]?.[0] ?? "")).toUpperCase();
18
+ }
5
19
  function UserMenu({
6
20
  signOutCallbackUrl = "/",
7
21
  className
8
22
  }) {
9
23
  const state = useAppUser();
10
24
  if (state.kind === "anonymous") return null;
11
- const email = state.kind === "db" ? state.user.email : state.user.email;
12
- const name = state.kind === "db" ? state.user.name : state.user.name ?? state.user.email;
13
- return /* @__PURE__ */ jsxs(
14
- "div",
15
- {
16
- className: ["hidden items-center gap-2 md:flex", className].filter(Boolean).join(" "),
17
- children: [
18
- /* @__PURE__ */ jsxs("div", { className: "text-right text-xs leading-tight", children: [
19
- /* @__PURE__ */ jsx("p", { className: "font-medium text-foreground", children: name ?? email }),
20
- email && name && name !== email ? /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: email }) : null
21
- ] }),
22
- /* @__PURE__ */ jsx(SignOutButton, { callbackUrl: signOutCallbackUrl })
23
- ]
24
- }
25
- );
25
+ const email = state.user.email;
26
+ const name = state.kind === "db" ? state.user.name : state.user.name ?? null;
27
+ const display = name && name !== email ? name : email;
28
+ const initials = initialsFor(name ?? null, email);
29
+ const triggerClass = [
30
+ "inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-full border border-border bg-muted text-sm font-semibold text-foreground transition hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
31
+ className
32
+ ].filter(Boolean).join(" ");
33
+ return /* @__PURE__ */ jsxs(DM.Root, { children: [
34
+ /* @__PURE__ */ jsx(DM.Trigger, { asChild: true, children: /* @__PURE__ */ jsx("button", { type: "button", "aria-label": "User menu", className: triggerClass, children: initials }) }),
35
+ /* @__PURE__ */ jsx(DM.Portal, { children: /* @__PURE__ */ jsxs(
36
+ DM.Content,
37
+ {
38
+ align: "end",
39
+ sideOffset: 6,
40
+ className: "z-[100] min-w-56 overflow-hidden rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0",
41
+ children: [
42
+ /* @__PURE__ */ jsxs("div", { className: "px-2 py-2", children: [
43
+ /* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium text-foreground", children: display }),
44
+ name && email && name !== email ? /* @__PURE__ */ jsx("p", { className: "truncate text-xs text-muted-foreground", children: email }) : null
45
+ ] }),
46
+ /* @__PURE__ */ jsx(DM.Separator, { className: "-mx-1 my-1 h-px bg-border" }),
47
+ /* @__PURE__ */ jsxs(
48
+ DM.Item,
49
+ {
50
+ onSelect: (e) => {
51
+ e.preventDefault();
52
+ void signOut({ callbackUrl: signOutCallbackUrl });
53
+ },
54
+ className: "flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm text-foreground outline-none focus:bg-accent focus:text-accent-foreground",
55
+ children: [
56
+ /* @__PURE__ */ jsx(LogOut, { className: "h-4 w-4" }),
57
+ /* @__PURE__ */ jsx("span", { children: "Sign out" })
58
+ ]
59
+ }
60
+ )
61
+ ]
62
+ }
63
+ ) })
64
+ ] });
26
65
  }
27
66
  export {
28
67
  UserMenu
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/client/UserMenu.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { useAppUser } from \"./AppUserProvider.js\";\nimport { SignOutButton } from \"./SignOutButton.js\";\n\n// =============================================================================\n// UserMenu -- header chrome showing the current user's identity + sign-out.\n//\n// Renders nothing for anonymous state. For db / session state shows the\n// user's name (or email fallback) and a sign-out button. Keeps the layout\n// simple (no dropdown) so it works inside the AppShell header without extra\n// portal-positioning. Spokes that want a richer dropdown can compose\n// <SignOutButton> themselves.\n// =============================================================================\n\nexport function UserMenu({\n signOutCallbackUrl = \"/\",\n className,\n}: {\n signOutCallbackUrl?: string;\n className?: string;\n}) {\n const state = useAppUser();\n if (state.kind === \"anonymous\") return null;\n\n const email = state.kind === \"db\" ? state.user.email : state.user.email;\n const name =\n state.kind === \"db\" ? state.user.name : (state.user.name ?? state.user.email);\n\n return (\n <div\n className={[\"hidden items-center gap-2 md:flex\", className]\n .filter(Boolean)\n .join(\" \")}\n >\n <div className=\"text-right text-xs leading-tight\">\n <p className=\"font-medium text-foreground\">{name ?? email}</p>\n {email && name && name !== email ? (\n <p className=\"text-muted-foreground\">{email}</p>\n ) : null}\n </div>\n <SignOutButton callbackUrl={signOutCallbackUrl} />\n </div>\n );\n}\n"],"mappings":";AAoCM,SACE,KADF;AAjCN,SAAS,kBAAkB;AAC3B,SAAS,qBAAqB;AAYvB,SAAS,SAAS;AAAA,EACvB,qBAAqB;AAAA,EACrB;AACF,GAGG;AACD,QAAM,QAAQ,WAAW;AACzB,MAAI,MAAM,SAAS,YAAa,QAAO;AAEvC,QAAM,QAAQ,MAAM,SAAS,OAAO,MAAM,KAAK,QAAQ,MAAM,KAAK;AAClE,QAAM,OACJ,MAAM,SAAS,OAAO,MAAM,KAAK,OAAQ,MAAM,KAAK,QAAQ,MAAM,KAAK;AAEzE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,CAAC,qCAAqC,SAAS,EACvD,OAAO,OAAO,EACd,KAAK,GAAG;AAAA,MAEX;AAAA,6BAAC,SAAI,WAAU,oCACb;AAAA,8BAAC,OAAE,WAAU,+BAA+B,kBAAQ,OAAM;AAAA,UACzD,SAAS,QAAQ,SAAS,QACzB,oBAAC,OAAE,WAAU,yBAAyB,iBAAM,IAC1C;AAAA,WACN;AAAA,QACA,oBAAC,iBAAc,aAAa,oBAAoB;AAAA;AAAA;AAAA,EAClD;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../src/client/UserMenu.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { LogOut } from \"lucide-react\";\nimport { DropdownMenu as DM } from \"radix-ui\";\nimport { signOut } from \"next-auth/react\";\nimport { useAppUser } from \"./AppUserProvider.js\";\n\n// =============================================================================\n// UserMenu -- compact identity + sign-out surface for the AppShell header.\n//\n// Renders as an avatar/initials button (h-9 w-9) that opens a Radix\n// DropdownMenu containing the user's display name, email, and a Sign out\n// item. Keyboard a11y is handled by Radix: Tab to the trigger, Enter/Space\n// to open, ArrowUp/Down between items, Enter to activate, Esc to close\n// and restore focus to the trigger.\n//\n// This component used to render name + email + Sign-out inline. That\n// worked inside the old 264px sidebar footer but overflowed the new\n// horizontal header. The current shape consumes a fixed 36x36 slot\n// regardless of name/email length.\n// =============================================================================\n\nfunction initialsFor(nameOrEmail: string | null | undefined, email: string): string {\n const source = (nameOrEmail || email || \"?\").trim();\n if (!source) return \"?\";\n if (source.includes(\"@\")) {\n const local = source.split(\"@\")[0] ?? \"\";\n return local.slice(0, 2).toUpperCase() || \"?\";\n }\n const parts = source.split(/\\s+/).filter(Boolean);\n if (parts.length === 0) return \"?\";\n if (parts.length === 1) return (parts[0]?.slice(0, 2) ?? \"?\").toUpperCase();\n return ((parts[0]?.[0] ?? \"\") + (parts[1]?.[0] ?? \"\")).toUpperCase();\n}\n\nexport function UserMenu({\n signOutCallbackUrl = \"/\",\n className,\n}: {\n signOutCallbackUrl?: string;\n className?: string;\n}) {\n const state = useAppUser();\n if (state.kind === \"anonymous\") return null;\n\n const email = state.user.email;\n const name = state.kind === \"db\" ? state.user.name : (state.user.name ?? null);\n const display = name && name !== email ? name : email;\n const initials = initialsFor(name ?? null, email);\n\n const triggerClass = [\n \"inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-full border border-border bg-muted text-sm font-semibold text-foreground transition hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n className,\n ]\n .filter(Boolean)\n .join(\" \");\n\n return (\n <DM.Root>\n <DM.Trigger asChild>\n <button type=\"button\" aria-label=\"User menu\" className={triggerClass}>\n {initials}\n </button>\n </DM.Trigger>\n <DM.Portal>\n <DM.Content\n align=\"end\"\n sideOffset={6}\n className=\"z-[100] min-w-56 overflow-hidden rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0\"\n >\n <div className=\"px-2 py-2\">\n <p className=\"truncate text-sm font-medium text-foreground\">{display}</p>\n {name && email && name !== email ? (\n <p className=\"truncate text-xs text-muted-foreground\">{email}</p>\n ) : null}\n </div>\n <DM.Separator className=\"-mx-1 my-1 h-px bg-border\" />\n <DM.Item\n onSelect={(e) => {\n e.preventDefault();\n void signOut({ callbackUrl: signOutCallbackUrl });\n }}\n className=\"flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm text-foreground outline-none focus:bg-accent focus:text-accent-foreground\"\n >\n <LogOut className=\"h-4 w-4\" />\n <span>Sign out</span>\n </DM.Item>\n </DM.Content>\n </DM.Portal>\n </DM.Root>\n );\n}\n"],"mappings":";AA6DQ,cAUE,YAVF;AA1DR,SAAS,cAAc;AACvB,SAAS,gBAAgB,UAAU;AACnC,SAAS,eAAe;AACxB,SAAS,kBAAkB;AAiB3B,SAAS,YAAY,aAAwC,OAAuB;AAClF,QAAM,UAAU,eAAe,SAAS,KAAK,KAAK;AAClD,MAAI,CAAC,OAAQ,QAAO;AACpB,MAAI,OAAO,SAAS,GAAG,GAAG;AACxB,UAAM,QAAQ,OAAO,MAAM,GAAG,EAAE,CAAC,KAAK;AACtC,WAAO,MAAM,MAAM,GAAG,CAAC,EAAE,YAAY,KAAK;AAAA,EAC5C;AACA,QAAM,QAAQ,OAAO,MAAM,KAAK,EAAE,OAAO,OAAO;AAChD,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,MAAI,MAAM,WAAW,EAAG,SAAQ,MAAM,CAAC,GAAG,MAAM,GAAG,CAAC,KAAK,KAAK,YAAY;AAC1E,WAAS,MAAM,CAAC,IAAI,CAAC,KAAK,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,KAAK,YAAY;AACrE;AAEO,SAAS,SAAS;AAAA,EACvB,qBAAqB;AAAA,EACrB;AACF,GAGG;AACD,QAAM,QAAQ,WAAW;AACzB,MAAI,MAAM,SAAS,YAAa,QAAO;AAEvC,QAAM,QAAQ,MAAM,KAAK;AACzB,QAAM,OAAO,MAAM,SAAS,OAAO,MAAM,KAAK,OAAQ,MAAM,KAAK,QAAQ;AACzE,QAAM,UAAU,QAAQ,SAAS,QAAQ,OAAO;AAChD,QAAM,WAAW,YAAY,QAAQ,MAAM,KAAK;AAEhD,QAAM,eAAe;AAAA,IACnB;AAAA,IACA;AAAA,EACF,EACG,OAAO,OAAO,EACd,KAAK,GAAG;AAEX,SACE,qBAAC,GAAG,MAAH,EACC;AAAA,wBAAC,GAAG,SAAH,EAAW,SAAO,MACjB,8BAAC,YAAO,MAAK,UAAS,cAAW,aAAY,WAAW,cACrD,oBACH,GACF;AAAA,IACA,oBAAC,GAAG,QAAH,EACC;AAAA,MAAC,GAAG;AAAA,MAAH;AAAA,QACC,OAAM;AAAA,QACN,YAAY;AAAA,QACZ,WAAU;AAAA,QAEV;AAAA,+BAAC,SAAI,WAAU,aACb;AAAA,gCAAC,OAAE,WAAU,gDAAgD,mBAAQ;AAAA,YACpE,QAAQ,SAAS,SAAS,QACzB,oBAAC,OAAE,WAAU,0CAA0C,iBAAM,IAC3D;AAAA,aACN;AAAA,UACA,oBAAC,GAAG,WAAH,EAAa,WAAU,6BAA4B;AAAA,UACpD;AAAA,YAAC,GAAG;AAAA,YAAH;AAAA,cACC,UAAU,CAAC,MAAM;AACf,kBAAE,eAAe;AACjB,qBAAK,QAAQ,EAAE,aAAa,mBAAmB,CAAC;AAAA,cAClD;AAAA,cACA,WAAU;AAAA,cAEV;AAAA,oCAAC,UAAO,WAAU,WAAU;AAAA,gBAC5B,oBAAC,UAAK,sBAAQ;AAAA;AAAA;AAAA,UAChB;AAAA;AAAA;AAAA,IACF,GACF;AAAA,KACF;AAEJ;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@augmenting-integrations/auth",
3
- "version": "8.14.0",
3
+ "version": "8.14.2",
4
4
  "description": "Auth.js v5 factory + JIT user provisioning + impersonation + client-side user menu / sign-out. Subpath exports: /server (createAuth, JIT, impersonation token mint/verify) and /client (AppUserProvider, useAppUser, UserMenu, SignOutButton, ImpersonationBanner).",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
@@ -28,14 +28,15 @@
28
28
  ],
29
29
  "dependencies": {
30
30
  "lucide-react": "^1.14.0",
31
+ "radix-ui": "^1.4.3",
31
32
  "zod": "^4.1.7"
32
33
  },
33
34
  "peerDependencies": {
34
35
  "next": "^16.0.0",
35
36
  "next-auth": "^5.0.0-beta.31",
36
37
  "react": "^19.0.0",
37
- "@augmenting-integrations/platform": "8.14.0",
38
- "@augmenting-integrations/aws": "8.14.0"
38
+ "@augmenting-integrations/aws": "8.14.2",
39
+ "@augmenting-integrations/platform": "8.14.2"
39
40
  },
40
41
  "devDependencies": {
41
42
  "@types/react": "^19.0.0",
@@ -46,8 +47,8 @@
46
47
  "tsup": "^8.3.5",
47
48
  "typescript": "^5.7.2",
48
49
  "vitest": "^4.1.5",
49
- "@augmenting-integrations/aws": "8.14.0",
50
- "@augmenting-integrations/platform": "8.14.0"
50
+ "@augmenting-integrations/aws": "8.14.2",
51
+ "@augmenting-integrations/platform": "8.14.2"
51
52
  },
52
53
  "scripts": {
53
54
  "build": "tsup",