@silicajs/components 0.1.1 → 0.1.3

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.
@@ -0,0 +1,7 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ declare function GoogleIcon({ className }: {
4
+ className?: string;
5
+ }): react_jsx_runtime.JSX.Element;
6
+
7
+ export { GoogleIcon };
@@ -0,0 +1,46 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ function GoogleIcon({ className }) {
3
+ return /* @__PURE__ */ jsxs(
4
+ "svg",
5
+ {
6
+ className,
7
+ viewBox: "0 0 24 24",
8
+ "aria-hidden": "true",
9
+ focusable: "false",
10
+ children: [
11
+ /* @__PURE__ */ jsx(
12
+ "path",
13
+ {
14
+ d: "M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z",
15
+ fill: "#4285F4"
16
+ }
17
+ ),
18
+ /* @__PURE__ */ jsx(
19
+ "path",
20
+ {
21
+ d: "M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z",
22
+ fill: "#34A853"
23
+ }
24
+ ),
25
+ /* @__PURE__ */ jsx(
26
+ "path",
27
+ {
28
+ d: "M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z",
29
+ fill: "#FBBC05"
30
+ }
31
+ ),
32
+ /* @__PURE__ */ jsx(
33
+ "path",
34
+ {
35
+ d: "M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z",
36
+ fill: "#EA4335"
37
+ }
38
+ )
39
+ ]
40
+ }
41
+ );
42
+ }
43
+ export {
44
+ GoogleIcon
45
+ };
46
+ //# sourceMappingURL=google-icon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/google-icon.tsx"],"sourcesContent":["export function GoogleIcon({ className }: { className?: string }) {\n return (\n <svg\n className={className}\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n focusable=\"false\"\n >\n <path\n d=\"M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z\"\n fill=\"#4285F4\"\n />\n <path\n d=\"M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z\"\n fill=\"#34A853\"\n />\n <path\n d=\"M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z\"\n fill=\"#FBBC05\"\n />\n <path\n d=\"M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z\"\n fill=\"#EA4335\"\n />\n </svg>\n );\n}\n"],"mappings":"AAEI,SAME,KANF;AAFG,SAAS,WAAW,EAAE,UAAU,GAA2B;AAChE,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,SAAQ;AAAA,MACR,eAAY;AAAA,MACZ,WAAU;AAAA,MAEV;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,GAAE;AAAA,YACF,MAAK;AAAA;AAAA,QACP;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,GAAE;AAAA,YACF,MAAK;AAAA;AAAA,QACP;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,GAAE;AAAA,YACF,MAAK;AAAA;AAAA,QACP;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,GAAE;AAAA,YACF,MAAK;AAAA;AAAA,QACP;AAAA;AAAA;AAAA,EACF;AAEJ;","names":[]}
package/dist/index.d.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  export { Backlinks, BacklinksProps } from './backlinks.js';
2
2
  export { Breadcrumbs, BreadcrumbsProps } from './breadcrumbs.js';
3
3
  export { DarkModeToggle, DarkModeToggleProps } from './dark-mode-toggle.js';
4
+ export { GoogleIcon } from './google-icon.js';
4
5
  export { NotAllowed, NotAllowedProps } from './not-allowed.js';
6
+ export { SignInShell, SignInShellProps } from './sign-in-shell.js';
5
7
  export { NotFound, NotFoundProps } from './not-found.js';
6
8
  export { PageProperties, PagePropertiesProps } from './page-properties.js';
7
9
  export { SearchPalette, SearchPaletteProps, SearchTrigger, SearchTriggerProps } from './search.js';
package/dist/index.js CHANGED
@@ -3,7 +3,9 @@ import { Breadcrumbs } from "./breadcrumbs.js";
3
3
  import {
4
4
  DarkModeToggle
5
5
  } from "./dark-mode-toggle.js";
6
+ import { GoogleIcon } from "./google-icon.js";
6
7
  import { NotAllowed } from "./not-allowed.js";
8
+ import { SignInShell } from "./sign-in-shell.js";
7
9
  import { NotFound } from "./not-found.js";
8
10
  import { PageProperties } from "./page-properties.js";
9
11
  import {
@@ -26,11 +28,13 @@ export {
26
28
  Backlinks,
27
29
  Breadcrumbs,
28
30
  DarkModeToggle,
31
+ GoogleIcon,
29
32
  NotAllowed,
30
33
  NotFound,
31
34
  PageProperties,
32
35
  SearchPalette,
33
36
  SearchTrigger,
37
+ SignInShell,
34
38
  SilicaLink,
35
39
  SilicaRoutingProvider,
36
40
  TableOfContents,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export { Backlinks, type BacklinksProps } from \"./backlinks.js\";\nexport { Breadcrumbs, type BreadcrumbsProps } from \"./breadcrumbs.js\";\nexport {\n DarkModeToggle,\n type DarkModeToggleProps,\n} from \"./dark-mode-toggle.js\";\nexport { NotAllowed, type NotAllowedProps } from \"./not-allowed.js\";\nexport { NotFound, type NotFoundProps } from \"./not-found.js\";\nexport { PageProperties, type PagePropertiesProps } from \"./page-properties.js\";\nexport {\n SearchPalette,\n SearchTrigger,\n type SearchPaletteProps,\n type SearchTriggerProps,\n} from \"./search.js\";\nexport {\n SilicaLink,\n SilicaRoutingProvider,\n useSilicaRouting,\n type SilicaLinkComponent,\n type SilicaLinkProps,\n type SilicaRoutingContextValue,\n type SilicaRoutingProviderProps,\n} from \"./routing.js\";\nexport {\n TableOfContents,\n type TableOfContentsProps,\n} from \"./table-of-contents.js\";\nexport { TagsList, type TagsListProps } from \"./tags-list.js\";\nexport { UserMenu, type UserMenuProps } from \"./user-menu.js\";\nexport { VaultTree, type VaultTreeProps } from \"./vault-tree.js\";\nexport { breadcrumbSegmentHref, prettySegment, slugToHref } from \"./slug.js\";\n"],"mappings":"AAAA,SAAS,iBAAsC;AAC/C,SAAS,mBAA0C;AACnD;AAAA,EACE;AAAA,OAEK;AACP,SAAS,kBAAwC;AACjD,SAAS,gBAAoC;AAC7C,SAAS,sBAAgD;AACzD;AAAA,EACE;AAAA,EACA;AAAA,OAGK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAKK;AACP;AAAA,EACE;AAAA,OAEK;AACP,SAAS,gBAAoC;AAC7C,SAAS,gBAAoC;AAC7C,SAAS,iBAAsC;AAC/C,SAAS,uBAAuB,eAAe,kBAAkB;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export { Backlinks, type BacklinksProps } from \"./backlinks.js\";\nexport { Breadcrumbs, type BreadcrumbsProps } from \"./breadcrumbs.js\";\nexport {\n DarkModeToggle,\n type DarkModeToggleProps,\n} from \"./dark-mode-toggle.js\";\nexport { GoogleIcon } from \"./google-icon.js\";\nexport { NotAllowed, type NotAllowedProps } from \"./not-allowed.js\";\nexport { SignInShell, type SignInShellProps } from \"./sign-in-shell.js\";\nexport { NotFound, type NotFoundProps } from \"./not-found.js\";\nexport { PageProperties, type PagePropertiesProps } from \"./page-properties.js\";\nexport {\n SearchPalette,\n SearchTrigger,\n type SearchPaletteProps,\n type SearchTriggerProps,\n} from \"./search.js\";\nexport {\n SilicaLink,\n SilicaRoutingProvider,\n useSilicaRouting,\n type SilicaLinkComponent,\n type SilicaLinkProps,\n type SilicaRoutingContextValue,\n type SilicaRoutingProviderProps,\n} from \"./routing.js\";\nexport {\n TableOfContents,\n type TableOfContentsProps,\n} from \"./table-of-contents.js\";\nexport { TagsList, type TagsListProps } from \"./tags-list.js\";\nexport { UserMenu, type UserMenuProps } from \"./user-menu.js\";\nexport { VaultTree, type VaultTreeProps } from \"./vault-tree.js\";\nexport { breadcrumbSegmentHref, prettySegment, slugToHref } from \"./slug.js\";\n"],"mappings":"AAAA,SAAS,iBAAsC;AAC/C,SAAS,mBAA0C;AACnD;AAAA,EACE;AAAA,OAEK;AACP,SAAS,kBAAkB;AAC3B,SAAS,kBAAwC;AACjD,SAAS,mBAA0C;AACnD,SAAS,gBAAoC;AAC7C,SAAS,sBAAgD;AACzD;AAAA,EACE;AAAA,EACA;AAAA,OAGK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAKK;AACP;AAAA,EACE;AAAA,OAEK;AACP,SAAS,gBAAoC;AAC7C,SAAS,gBAAoC;AAC7C,SAAS,iBAAsC;AAC/C,SAAS,uBAAuB,eAAe,kBAAkB;","names":[]}
package/dist/not-found.js CHANGED
@@ -18,7 +18,13 @@ function NotFound({
18
18
  /* @__PURE__ */ jsx(CardTitle, { className: "text-lg", children: title }),
19
19
  /* @__PURE__ */ jsx(CardDescription, { children: description })
20
20
  ] }),
21
- /* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsx(Button, { variant: "outline", render: /* @__PURE__ */ jsx("a", { href: cta.href, children: cta.label }) }) })
21
+ /* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsx(
22
+ Button,
23
+ {
24
+ variant: "outline",
25
+ render: /* @__PURE__ */ jsx("a", { href: cta.href, children: cta.label })
26
+ }
27
+ ) })
22
28
  ] }) });
23
29
  }
24
30
  export {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/not-found.tsx"],"sourcesContent":["import { Button } from \"@silicajs/ui/components/button\";\nimport {\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n} from \"@silicajs/ui/components/card\";\n\nexport type NotFoundProps = {\n title?: string;\n description?: string;\n cta?: { href: string; label: string };\n className?: string;\n};\n\nexport function NotFound({\n title = \"Page not found\",\n description = \"The requested note does not exist or is not published.\",\n cta = { href: \"/\", label: \"Return home\" },\n className,\n}: NotFoundProps) {\n return (\n <main className={className} data-slot=\"not-found\">\n <Card className=\"mx-auto max-w-md\">\n <CardHeader>\n <CardTitle className=\"text-lg\">{title}</CardTitle>\n <CardDescription>{description}</CardDescription>\n </CardHeader>\n <CardContent>\n <Button variant=\"outline\" render={<a href={cta.href}>{cta.label}</a>} />\n </CardContent>\n </Card>\n </main>\n );\n}\n"],"mappings":"AAyBQ,SACE,KADF;AAzBR,SAAS,cAAc;AACvB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AASA,SAAS,SAAS;AAAA,EACvB,QAAQ;AAAA,EACR,cAAc;AAAA,EACd,MAAM,EAAE,MAAM,KAAK,OAAO,cAAc;AAAA,EACxC;AACF,GAAkB;AAChB,SACE,oBAAC,UAAK,WAAsB,aAAU,aACpC,+BAAC,QAAK,WAAU,oBACd;AAAA,yBAAC,cACC;AAAA,0BAAC,aAAU,WAAU,WAAW,iBAAM;AAAA,MACtC,oBAAC,mBAAiB,uBAAY;AAAA,OAChC;AAAA,IACA,oBAAC,eACC,8BAAC,UAAO,SAAQ,WAAU,QAAQ,oBAAC,OAAE,MAAM,IAAI,MAAO,cAAI,OAAM,GAAM,GACxE;AAAA,KACF,GACF;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../src/not-found.tsx"],"sourcesContent":["import { Button } from \"@silicajs/ui/components/button\";\nimport {\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n} from \"@silicajs/ui/components/card\";\n\nexport type NotFoundProps = {\n title?: string;\n description?: string;\n cta?: { href: string; label: string };\n className?: string;\n};\n\nexport function NotFound({\n title = \"Page not found\",\n description = \"The requested note does not exist or is not published.\",\n cta = { href: \"/\", label: \"Return home\" },\n className,\n}: NotFoundProps) {\n return (\n <main className={className} data-slot=\"not-found\">\n <Card className=\"mx-auto max-w-md\">\n <CardHeader>\n <CardTitle className=\"text-lg\">{title}</CardTitle>\n <CardDescription>{description}</CardDescription>\n </CardHeader>\n <CardContent>\n <Button\n variant=\"outline\"\n render={<a href={cta.href}>{cta.label}</a>}\n />\n </CardContent>\n </Card>\n </main>\n );\n}\n"],"mappings":"AAyBQ,SACE,KADF;AAzBR,SAAS,cAAc;AACvB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AASA,SAAS,SAAS;AAAA,EACvB,QAAQ;AAAA,EACR,cAAc;AAAA,EACd,MAAM,EAAE,MAAM,KAAK,OAAO,cAAc;AAAA,EACxC;AACF,GAAkB;AAChB,SACE,oBAAC,UAAK,WAAsB,aAAU,aACpC,+BAAC,QAAK,WAAU,oBACd;AAAA,yBAAC,cACC;AAAA,0BAAC,aAAU,WAAU,WAAW,iBAAM;AAAA,MACtC,oBAAC,mBAAiB,uBAAY;AAAA,OAChC;AAAA,IACA,oBAAC,eACC;AAAA,MAAC;AAAA;AAAA,QACC,SAAQ;AAAA,QACR,QAAQ,oBAAC,OAAE,MAAM,IAAI,MAAO,cAAI,OAAM;AAAA;AAAA,IACxC,GACF;AAAA,KACF,GACF;AAEJ;","names":[]}
package/dist/search.js CHANGED
@@ -139,8 +139,8 @@ function SearchPalette({ open, onOpenChange }) {
139
139
  close();
140
140
  },
141
141
  children: /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col gap-0.5", children: [
142
- /* @__PURE__ */ jsx("span", { className: "truncate font-medium text-foreground", children: result.title }),
143
- result.excerpt ? /* @__PURE__ */ jsx("span", { className: "truncate text-xs text-muted-foreground", children: result.excerpt }) : null
142
+ /* @__PURE__ */ jsx("span", { className: "truncate font-medium text-foreground", children: /* @__PURE__ */ jsx(HighlightedText, { parts: result.titleParts }) }),
143
+ result.excerptParts.length > 0 ? /* @__PURE__ */ jsx("span", { className: "truncate text-xs text-muted-foreground", children: /* @__PURE__ */ jsx(HighlightedText, { parts: result.excerptParts }) }) : null
144
144
  ] })
145
145
  },
146
146
  result.slug
@@ -151,6 +151,18 @@ function SearchPalette({ open, onOpenChange }) {
151
151
  )
152
152
  ] });
153
153
  }
154
+ function HighlightedText({ parts }) {
155
+ return /* @__PURE__ */ jsx(Fragment, { children: parts.map(
156
+ (part, index) => part.highlighted ? /* @__PURE__ */ jsx(
157
+ "mark",
158
+ {
159
+ className: "bg-transparent p-0 font-medium text-primary",
160
+ children: part.text
161
+ },
162
+ index
163
+ ) : /* @__PURE__ */ jsx(React.Fragment, { children: part.text }, index)
164
+ ) });
165
+ }
154
166
  export {
155
167
  SearchPalette,
156
168
  SearchTrigger
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/search.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\n\nimport {\n Command,\n CommandEmpty,\n CommandInput,\n CommandItem,\n CommandList,\n} from \"@silicajs/ui/components/command\";\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n} from \"@silicajs/ui/components/dialog\";\nimport { Button } from \"@silicajs/ui/components/button\";\nimport { SearchIcon } from \"lucide-react\";\n\nimport { useSilicaRouting } from \"./routing.js\";\nimport { slugToHref } from \"./slug.js\";\n\ntype SearchResult = {\n slug: string;\n title: string;\n excerpt: string;\n};\n\nexport type SearchTriggerProps = {\n placeholder?: string;\n className?: string;\n};\n\nexport function SearchTrigger({\n placeholder = \"Search…\",\n className,\n}: SearchTriggerProps) {\n const [open, setOpen] = React.useState(false);\n\n React.useEffect(() => {\n const onKeyDown = (event: KeyboardEvent) => {\n if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === \"k\") {\n event.preventDefault();\n setOpen((value) => !value);\n }\n };\n window.addEventListener(\"keydown\", onKeyDown);\n return () => window.removeEventListener(\"keydown\", onKeyDown);\n }, []);\n\n return (\n <>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setOpen(true)}\n className={className}\n >\n <SearchIcon\n data-icon=\"inline-start\"\n className=\"text-muted-foreground\"\n />\n <span className=\"flex-1 text-left text-muted-foreground\">\n {placeholder}\n </span>\n <kbd\n data-icon=\"inline-end\"\n className=\"pointer-events-none ml-2 inline-flex h-5 select-none items-center gap-1 rounded border border-border bg-muted px-1.5 font-mono text-[10px] font-medium text-muted-foreground\"\n >\n <span className=\"text-xs\">⌘</span>K\n </kbd>\n </Button>\n <SearchPalette open={open} onOpenChange={setOpen} />\n </>\n );\n}\n\nexport type SearchPaletteProps = {\n open: boolean;\n onOpenChange: (next: boolean) => void;\n};\n\nexport function SearchPalette({ open, onOpenChange }: SearchPaletteProps) {\n const { navigate } = useSilicaRouting();\n const [query, setQuery] = React.useState(\"\");\n const [results, setResults] = React.useState<SearchResult[]>([]);\n const [isLoading, setIsLoading] = React.useState(false);\n\n React.useEffect(() => {\n if (!open) {\n setQuery(\"\");\n setResults([]);\n setIsLoading(false);\n }\n }, [open]);\n\n React.useEffect(() => {\n const controller = new AbortController();\n const trimmed = query.trim();\n if (!trimmed) {\n setResults([]);\n setIsLoading(false);\n return () => controller.abort();\n }\n\n setIsLoading(true);\n const timeout = window.setTimeout(() => {\n fetch(`/api/search?q=${encodeURIComponent(trimmed)}`, {\n signal: controller.signal,\n })\n .then((response) => (response.ok ? response.json() : { results: [] }))\n .then((payload: { results?: SearchResult[] }) => {\n setResults(payload.results ?? []);\n })\n .catch((error: unknown) => {\n if (error instanceof DOMException && error.name === \"AbortError\")\n return;\n setResults([]);\n })\n .finally(() => setIsLoading(false));\n }, 120);\n\n return () => {\n window.clearTimeout(timeout);\n controller.abort();\n };\n }, [query]);\n\n const close = () => onOpenChange(false);\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogHeader className=\"sr-only\">\n <DialogTitle>Search</DialogTitle>\n <DialogDescription>Search your vault.</DialogDescription>\n </DialogHeader>\n <DialogContent\n className=\"top-1/3 translate-y-0 overflow-hidden rounded-xl! p-0\"\n showCloseButton={false}\n >\n <Command shouldFilter={false}>\n <CommandInput\n autoFocus\n placeholder=\"Search your vault…\"\n value={query}\n onValueChange={setQuery}\n />\n <CommandList>\n {isLoading ? (\n <div className=\"px-4 py-6 text-center text-sm text-muted-foreground\">\n Searching…\n </div>\n ) : null}\n {!isLoading && query.trim() && results.length === 0 ? (\n <CommandEmpty>No results</CommandEmpty>\n ) : null}\n {results.map((result) => (\n <CommandItem\n key={result.slug}\n value={`${result.title} ${result.slug}`}\n onSelect={() => {\n navigate(slugToHref(result.slug));\n close();\n }}\n >\n <div className=\"flex min-w-0 flex-col gap-0.5\">\n <span className=\"truncate font-medium text-foreground\">\n {result.title}\n </span>\n {result.excerpt ? (\n <span className=\"truncate text-xs text-muted-foreground\">\n {result.excerpt}\n </span>\n ) : null}\n </div>\n </CommandItem>\n ))}\n </CommandList>\n </Command>\n </DialogContent>\n </Dialog>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/search.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\n\nimport {\n Command,\n CommandEmpty,\n CommandInput,\n CommandItem,\n CommandList,\n} from \"@silicajs/ui/components/command\";\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n} from \"@silicajs/ui/components/dialog\";\nimport { Button } from \"@silicajs/ui/components/button\";\nimport { SearchIcon } from \"lucide-react\";\n\nimport { useSilicaRouting } from \"./routing.js\";\nimport { slugToHref } from \"./slug.js\";\n\ntype SearchHighlightPart = {\n text: string;\n highlighted: boolean;\n};\n\ntype SearchResult = {\n slug: string;\n title: string;\n titleParts: SearchHighlightPart[];\n excerptParts: SearchHighlightPart[];\n};\n\nexport type SearchTriggerProps = {\n placeholder?: string;\n className?: string;\n};\n\nexport function SearchTrigger({\n placeholder = \"Search…\",\n className,\n}: SearchTriggerProps) {\n const [open, setOpen] = React.useState(false);\n\n React.useEffect(() => {\n const onKeyDown = (event: KeyboardEvent) => {\n if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === \"k\") {\n event.preventDefault();\n setOpen((value) => !value);\n }\n };\n window.addEventListener(\"keydown\", onKeyDown);\n return () => window.removeEventListener(\"keydown\", onKeyDown);\n }, []);\n\n return (\n <>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setOpen(true)}\n className={className}\n >\n <SearchIcon\n data-icon=\"inline-start\"\n className=\"text-muted-foreground\"\n />\n <span className=\"flex-1 text-left text-muted-foreground\">\n {placeholder}\n </span>\n <kbd\n data-icon=\"inline-end\"\n className=\"pointer-events-none ml-2 inline-flex h-5 select-none items-center gap-1 rounded border border-border bg-muted px-1.5 font-mono text-[10px] font-medium text-muted-foreground\"\n >\n <span className=\"text-xs\">⌘</span>K\n </kbd>\n </Button>\n <SearchPalette open={open} onOpenChange={setOpen} />\n </>\n );\n}\n\nexport type SearchPaletteProps = {\n open: boolean;\n onOpenChange: (next: boolean) => void;\n};\n\nexport function SearchPalette({ open, onOpenChange }: SearchPaletteProps) {\n const { navigate } = useSilicaRouting();\n const [query, setQuery] = React.useState(\"\");\n const [results, setResults] = React.useState<SearchResult[]>([]);\n const [isLoading, setIsLoading] = React.useState(false);\n\n React.useEffect(() => {\n if (!open) {\n setQuery(\"\");\n setResults([]);\n setIsLoading(false);\n }\n }, [open]);\n\n React.useEffect(() => {\n const controller = new AbortController();\n const trimmed = query.trim();\n if (!trimmed) {\n setResults([]);\n setIsLoading(false);\n return () => controller.abort();\n }\n\n setIsLoading(true);\n const timeout = window.setTimeout(() => {\n fetch(`/api/search?q=${encodeURIComponent(trimmed)}`, {\n signal: controller.signal,\n })\n .then((response) => (response.ok ? response.json() : { results: [] }))\n .then((payload: { results?: SearchResult[] }) => {\n setResults(payload.results ?? []);\n })\n .catch((error: unknown) => {\n if (error instanceof DOMException && error.name === \"AbortError\")\n return;\n setResults([]);\n })\n .finally(() => setIsLoading(false));\n }, 120);\n\n return () => {\n window.clearTimeout(timeout);\n controller.abort();\n };\n }, [query]);\n\n const close = () => onOpenChange(false);\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogHeader className=\"sr-only\">\n <DialogTitle>Search</DialogTitle>\n <DialogDescription>Search your vault.</DialogDescription>\n </DialogHeader>\n <DialogContent\n className=\"top-1/3 translate-y-0 overflow-hidden rounded-xl! p-0\"\n showCloseButton={false}\n >\n <Command shouldFilter={false}>\n <CommandInput\n autoFocus\n placeholder=\"Search your vault…\"\n value={query}\n onValueChange={setQuery}\n />\n <CommandList>\n {isLoading ? (\n <div className=\"px-4 py-6 text-center text-sm text-muted-foreground\">\n Searching…\n </div>\n ) : null}\n {!isLoading && query.trim() && results.length === 0 ? (\n <CommandEmpty>No results</CommandEmpty>\n ) : null}\n {results.map((result) => (\n <CommandItem\n key={result.slug}\n value={`${result.title} ${result.slug}`}\n onSelect={() => {\n navigate(slugToHref(result.slug));\n close();\n }}\n >\n <div className=\"flex min-w-0 flex-col gap-0.5\">\n <span className=\"truncate font-medium text-foreground\">\n <HighlightedText parts={result.titleParts} />\n </span>\n {result.excerptParts.length > 0 ? (\n <span className=\"truncate text-xs text-muted-foreground\">\n <HighlightedText parts={result.excerptParts} />\n </span>\n ) : null}\n </div>\n </CommandItem>\n ))}\n </CommandList>\n </Command>\n </DialogContent>\n </Dialog>\n );\n}\n\nfunction HighlightedText({ parts }: { parts: SearchHighlightPart[] }) {\n return (\n <>\n {parts.map((part, index) =>\n part.highlighted ? (\n <mark\n key={index}\n className=\"bg-transparent p-0 font-medium text-primary\"\n >\n {part.text}\n </mark>\n ) : (\n <React.Fragment key={index}>{part.text}</React.Fragment>\n ),\n )}\n </>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,14 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
3
+
4
+ type SignInShellProps = {
5
+ title: string;
6
+ description?: string;
7
+ logo?: string;
8
+ headline?: string;
9
+ subheadline?: string;
10
+ children: ReactNode;
11
+ };
12
+ declare function SignInShell({ title, description, logo, headline, subheadline, children, }: SignInShellProps): react_jsx_runtime.JSX.Element;
13
+
14
+ export { SignInShell, type SignInShellProps };
@@ -0,0 +1,50 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { BookOpen } from "lucide-react";
3
+ import {
4
+ Card,
5
+ CardContent,
6
+ CardDescription,
7
+ CardHeader,
8
+ CardTitle
9
+ } from "@silicajs/ui/components/card";
10
+ function SignInShell({
11
+ title,
12
+ description,
13
+ logo,
14
+ headline = "Sign in required",
15
+ subheadline,
16
+ children
17
+ }) {
18
+ const hint = subheadline ?? (description ? `${description} is private. Sign in with Google to access it.` : "This site is private. Sign in with Google to continue.");
19
+ return /* @__PURE__ */ jsxs(
20
+ "main",
21
+ {
22
+ className: "flex min-h-svh flex-col items-center justify-center gap-6 bg-muted/40 p-6 md:p-10",
23
+ "data-slot": "sign-in",
24
+ children: [
25
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
26
+ logo ? /* @__PURE__ */ jsx(
27
+ "img",
28
+ {
29
+ src: logo,
30
+ alt: "",
31
+ className: "size-8 shrink-0 rounded-md object-contain"
32
+ }
33
+ ) : /* @__PURE__ */ jsx("span", { className: "flex size-8 shrink-0 items-center justify-center rounded-md border border-border bg-background text-foreground", children: /* @__PURE__ */ jsx(BookOpen, { className: "size-4", "aria-hidden": "true" }) }),
34
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold tracking-tight text-foreground", children: title })
35
+ ] }),
36
+ /* @__PURE__ */ jsxs(Card, { className: "w-full max-w-sm shadow-sm", children: [
37
+ /* @__PURE__ */ jsxs(CardHeader, { className: "text-center", children: [
38
+ /* @__PURE__ */ jsx(CardTitle, { className: "text-xl tracking-tight", children: headline }),
39
+ /* @__PURE__ */ jsx(CardDescription, { children: hint })
40
+ ] }),
41
+ /* @__PURE__ */ jsx(CardContent, { className: "flex flex-col gap-4", children })
42
+ ] })
43
+ ]
44
+ }
45
+ );
46
+ }
47
+ export {
48
+ SignInShell
49
+ };
50
+ //# sourceMappingURL=sign-in-shell.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/sign-in-shell.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { BookOpen } from \"lucide-react\";\nimport {\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n} from \"@silicajs/ui/components/card\";\n\nexport type SignInShellProps = {\n title: string;\n description?: string;\n logo?: string;\n headline?: string;\n subheadline?: string;\n children: ReactNode;\n};\n\nexport function SignInShell({\n title,\n description,\n logo,\n headline = \"Sign in required\",\n subheadline,\n children,\n}: SignInShellProps) {\n const hint =\n subheadline ??\n (description\n ? `${description} is private. Sign in with Google to access it.`\n : \"This site is private. Sign in with Google to continue.\");\n\n return (\n <main\n className=\"flex min-h-svh flex-col items-center justify-center gap-6 bg-muted/40 p-6 md:p-10\"\n data-slot=\"sign-in\"\n >\n <div className=\"flex items-center gap-2\">\n {logo ? (\n <img\n src={logo}\n alt=\"\"\n className=\"size-8 shrink-0 rounded-md object-contain\"\n />\n ) : (\n <span className=\"flex size-8 shrink-0 items-center justify-center rounded-md border border-border bg-background text-foreground\">\n <BookOpen className=\"size-4\" aria-hidden=\"true\" />\n </span>\n )}\n <span className=\"text-sm font-semibold tracking-tight text-foreground\">\n {title}\n </span>\n </div>\n\n <Card className=\"w-full max-w-sm shadow-sm\">\n <CardHeader className=\"text-center\">\n <CardTitle className=\"text-xl tracking-tight\">{headline}</CardTitle>\n <CardDescription>{hint}</CardDescription>\n </CardHeader>\n <CardContent className=\"flex flex-col gap-4\">{children}</CardContent>\n </Card>\n </main>\n );\n}\n"],"mappings":"AAsCM,SAEI,KAFJ;AArCN,SAAS,gBAAgB;AACzB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAWA,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA;AACF,GAAqB;AACnB,QAAM,OACJ,gBACC,cACG,GAAG,WAAW,mDACd;AAEN,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,aAAU;AAAA,MAEV;AAAA,6BAAC,SAAI,WAAU,2BACZ;AAAA,iBACC;AAAA,YAAC;AAAA;AAAA,cACC,KAAK;AAAA,cACL,KAAI;AAAA,cACJ,WAAU;AAAA;AAAA,UACZ,IAEA,oBAAC,UAAK,WAAU,kHACd,8BAAC,YAAS,WAAU,UAAS,eAAY,QAAO,GAClD;AAAA,UAEF,oBAAC,UAAK,WAAU,wDACb,iBACH;AAAA,WACF;AAAA,QAEA,qBAAC,QAAK,WAAU,6BACd;AAAA,+BAAC,cAAW,WAAU,eACpB;AAAA,gCAAC,aAAU,WAAU,0BAA0B,oBAAS;AAAA,YACxD,oBAAC,mBAAiB,gBAAK;AAAA,aACzB;AAAA,UACA,oBAAC,eAAY,WAAU,uBAAuB,UAAS;AAAA,WACzD;AAAA;AAAA;AAAA,EACF;AAEJ;","names":[]}
@@ -1,9 +1,8 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
 
3
3
  type UserMenuProps = {
4
- label?: string;
5
- className?: string;
4
+ logo?: string;
6
5
  };
7
- declare function UserMenu({ label, className }: UserMenuProps): react_jsx_runtime.JSX.Element;
6
+ declare function UserMenu({ logo }: UserMenuProps): react_jsx_runtime.JSX.Element;
8
7
 
9
8
  export { UserMenu, type UserMenuProps };
package/dist/user-menu.js CHANGED
@@ -1,40 +1,139 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
- import { UserIcon } from "lucide-react";
4
- import { Button } from "@silicajs/ui/components/button";
3
+ import { useEffect, useState } from "react";
4
+ import { EllipsisVertical, LogOut } from "lucide-react";
5
+ import {
6
+ Avatar,
7
+ AvatarFallback,
8
+ AvatarImage
9
+ } from "@silicajs/ui/components/avatar";
5
10
  import {
6
11
  DropdownMenu,
7
12
  DropdownMenuContent,
13
+ DropdownMenuGroup,
8
14
  DropdownMenuItem,
9
- DropdownMenuLabel,
10
15
  DropdownMenuSeparator,
11
16
  DropdownMenuTrigger
12
17
  } from "@silicajs/ui/components/dropdown-menu";
13
- function UserMenu({ label = "Signed in", className }) {
14
- return /* @__PURE__ */ jsxs(DropdownMenu, { children: [
15
- /* @__PURE__ */ jsx(
18
+ import {
19
+ SidebarMenu,
20
+ SidebarMenuButton,
21
+ SidebarMenuItem,
22
+ useSidebar
23
+ } from "@silicajs/ui/components/sidebar";
24
+ function UserMenu({ logo }) {
25
+ const { isMobile } = useSidebar();
26
+ const [user, setUser] = useState(null);
27
+ useEffect(() => {
28
+ let cancelled = false;
29
+ void fetch("/api/auth/get-session", { credentials: "include" }).then((response) => response.ok ? response.json() : null).then((data) => {
30
+ if (cancelled || !data?.user) return;
31
+ setUser({
32
+ name: data.user.name ?? data.user.email ?? "Account",
33
+ email: data.user.email ?? "",
34
+ image: data.user.image ?? void 0
35
+ });
36
+ }).catch(() => {
37
+ });
38
+ return () => {
39
+ cancelled = true;
40
+ };
41
+ }, []);
42
+ const displayName = user?.name ?? "Account";
43
+ const displayEmail = user?.email ?? "";
44
+ const avatarSrc = user?.image ?? logo;
45
+ const initials = getInitials(user?.name, user?.email);
46
+ return /* @__PURE__ */ jsx(SidebarMenu, { children: /* @__PURE__ */ jsx(SidebarMenuItem, { children: /* @__PURE__ */ jsxs(DropdownMenu, { children: [
47
+ /* @__PURE__ */ jsxs(
16
48
  DropdownMenuTrigger,
17
49
  {
18
50
  render: /* @__PURE__ */ jsx(
19
- Button,
51
+ SidebarMenuButton,
20
52
  {
21
- type: "button",
22
- variant: "ghost",
23
- size: "icon-sm",
24
- "aria-label": "User menu",
25
- className,
26
- children: /* @__PURE__ */ jsx(UserIcon, { "aria-hidden": "true" })
53
+ size: "lg",
54
+ className: "data-open:bg-sidebar-accent data-open:text-sidebar-accent-foreground"
27
55
  }
28
- )
56
+ ),
57
+ children: [
58
+ /* @__PURE__ */ jsx(UserAvatar, { src: avatarSrc, alt: displayName, initials }),
59
+ /* @__PURE__ */ jsxs("div", { className: "grid flex-1 text-left text-sm leading-tight", children: [
60
+ /* @__PURE__ */ jsx("span", { className: "truncate font-medium", children: displayName }),
61
+ displayEmail ? /* @__PURE__ */ jsx("span", { className: "truncate text-xs text-muted-foreground", children: displayEmail }) : null
62
+ ] }),
63
+ /* @__PURE__ */ jsx(EllipsisVertical, { className: "ml-auto size-4 shrink-0 text-muted-foreground" })
64
+ ]
29
65
  }
30
66
  ),
31
- /* @__PURE__ */ jsxs(DropdownMenuContent, { align: "end", sideOffset: 6, className: "min-w-44", children: [
32
- /* @__PURE__ */ jsx(DropdownMenuLabel, { children: label }),
33
- /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
34
- /* @__PURE__ */ jsx(DropdownMenuItem, { render: /* @__PURE__ */ jsx("a", { href: "/api/auth/sign-out", children: "Sign out" }) })
35
- ] })
67
+ /* @__PURE__ */ jsxs(
68
+ DropdownMenuContent,
69
+ {
70
+ className: "min-w-56 rounded-lg",
71
+ side: isMobile ? "bottom" : "right",
72
+ align: "end",
73
+ sideOffset: 4,
74
+ children: [
75
+ /* @__PURE__ */ jsx("div", { className: "px-1 py-1.5 font-normal", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-left text-sm", children: [
76
+ /* @__PURE__ */ jsx(
77
+ UserAvatar,
78
+ {
79
+ src: avatarSrc,
80
+ alt: displayName,
81
+ initials
82
+ }
83
+ ),
84
+ /* @__PURE__ */ jsxs("div", { className: "grid flex-1 text-left text-sm leading-tight", children: [
85
+ /* @__PURE__ */ jsx("span", { className: "truncate font-medium", children: displayName }),
86
+ displayEmail ? /* @__PURE__ */ jsx("span", { className: "truncate text-xs text-muted-foreground", children: displayEmail }) : null
87
+ ] })
88
+ ] }) }),
89
+ /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
90
+ /* @__PURE__ */ jsx(DropdownMenuGroup, { children: /* @__PURE__ */ jsxs(
91
+ DropdownMenuItem,
92
+ {
93
+ variant: "destructive",
94
+ onClick: () => {
95
+ void signOut();
96
+ },
97
+ children: [
98
+ /* @__PURE__ */ jsx(LogOut, {}),
99
+ "Sign out"
100
+ ]
101
+ }
102
+ ) })
103
+ ]
104
+ }
105
+ )
106
+ ] }) }) });
107
+ }
108
+ function UserAvatar({
109
+ src,
110
+ alt,
111
+ initials
112
+ }) {
113
+ return /* @__PURE__ */ jsxs(Avatar, { className: "size-8", children: [
114
+ src ? /* @__PURE__ */ jsx(AvatarImage, { src, alt }) : null,
115
+ /* @__PURE__ */ jsx(AvatarFallback, { className: "text-xs", children: initials })
36
116
  ] });
37
117
  }
118
+ function getInitials(name, email) {
119
+ const trimmedName = name?.trim();
120
+ if (trimmedName) {
121
+ const parts = trimmedName.split(/\s+/).filter(Boolean);
122
+ if (parts.length >= 2) {
123
+ return `${parts[0]?.[0] ?? ""}${parts[1]?.[0] ?? ""}`.toUpperCase();
124
+ }
125
+ return trimmedName.slice(0, 2).toUpperCase();
126
+ }
127
+ if (email) return email.slice(0, 2).toUpperCase();
128
+ return "??";
129
+ }
130
+ async function signOut() {
131
+ await fetch("/api/auth/sign-out", {
132
+ method: "POST",
133
+ credentials: "include"
134
+ });
135
+ window.location.assign("/sign-in");
136
+ }
38
137
  export {
39
138
  UserMenu
40
139
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/user-menu.tsx"],"sourcesContent":["\"use client\";\n\nimport { UserIcon } from \"lucide-react\";\n\nimport { Button } from \"@silicajs/ui/components/button\";\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from \"@silicajs/ui/components/dropdown-menu\";\n\nexport type UserMenuProps = {\n label?: string;\n className?: string;\n};\n\nexport function UserMenu({ label = \"Signed in\", className }: UserMenuProps) {\n return (\n <DropdownMenu>\n <DropdownMenuTrigger\n render={\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-sm\"\n aria-label=\"User menu\"\n className={className}\n >\n <UserIcon aria-hidden=\"true\" />\n </Button>\n }\n />\n <DropdownMenuContent align=\"end\" sideOffset={6} className=\"min-w-44\">\n <DropdownMenuLabel>{label}</DropdownMenuLabel>\n <DropdownMenuSeparator />\n <DropdownMenuItem render={<a href=\"/api/auth/sign-out\">Sign out</a>} />\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n"],"mappings":";AA+BY,cAIN,YAJM;AA7BZ,SAAS,gBAAgB;AAEzB,SAAS,cAAc;AACvB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAOA,SAAS,SAAS,EAAE,QAAQ,aAAa,UAAU,GAAkB;AAC1E,SACE,qBAAC,gBACC;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,QACE;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAQ;AAAA,YACR,MAAK;AAAA,YACL,cAAW;AAAA,YACX;AAAA,YAEA,8BAAC,YAAS,eAAY,QAAO;AAAA;AAAA,QAC/B;AAAA;AAAA,IAEJ;AAAA,IACA,qBAAC,uBAAoB,OAAM,OAAM,YAAY,GAAG,WAAU,YACxD;AAAA,0BAAC,qBAAmB,iBAAM;AAAA,MAC1B,oBAAC,yBAAsB;AAAA,MACvB,oBAAC,oBAAiB,QAAQ,oBAAC,OAAE,MAAK,sBAAqB,sBAAQ,GAAM;AAAA,OACvE;AAAA,KACF;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../src/user-menu.tsx"],"sourcesContent":["\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport { EllipsisVertical, LogOut } from \"lucide-react\";\n\nimport {\n Avatar,\n AvatarFallback,\n AvatarImage,\n} from \"@silicajs/ui/components/avatar\";\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuGroup,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from \"@silicajs/ui/components/dropdown-menu\";\nimport {\n SidebarMenu,\n SidebarMenuButton,\n SidebarMenuItem,\n useSidebar,\n} from \"@silicajs/ui/components/sidebar\";\n\nexport type UserMenuProps = {\n logo?: string;\n};\n\ntype SessionUser = {\n name?: string | null;\n email?: string | null;\n image?: string | null;\n};\n\ntype SessionResponse = {\n user?: SessionUser;\n};\n\nexport function UserMenu({ logo }: UserMenuProps) {\n const { isMobile } = useSidebar();\n const [user, setUser] = useState<SessionUser | null>(null);\n\n useEffect(() => {\n let cancelled = false;\n void fetch(\"/api/auth/get-session\", { credentials: \"include\" })\n .then((response) => (response.ok ? response.json() : null))\n .then((data: SessionResponse | null) => {\n if (cancelled || !data?.user) return;\n setUser({\n name: data.user.name ?? data.user.email ?? \"Account\",\n email: data.user.email ?? \"\",\n image: data.user.image ?? undefined,\n });\n })\n .catch(() => {});\n\n return () => {\n cancelled = true;\n };\n }, []);\n\n const displayName = user?.name ?? \"Account\";\n const displayEmail = user?.email ?? \"\";\n const avatarSrc = user?.image ?? logo;\n const initials = getInitials(user?.name, user?.email);\n\n return (\n <SidebarMenu>\n <SidebarMenuItem>\n <DropdownMenu>\n <DropdownMenuTrigger\n render={\n <SidebarMenuButton\n size=\"lg\"\n className=\"data-open:bg-sidebar-accent data-open:text-sidebar-accent-foreground\"\n />\n }\n >\n <UserAvatar src={avatarSrc} alt={displayName} initials={initials} />\n <div className=\"grid flex-1 text-left text-sm leading-tight\">\n <span className=\"truncate font-medium\">{displayName}</span>\n {displayEmail ? (\n <span className=\"truncate text-xs text-muted-foreground\">\n {displayEmail}\n </span>\n ) : null}\n </div>\n <EllipsisVertical className=\"ml-auto size-4 shrink-0 text-muted-foreground\" />\n </DropdownMenuTrigger>\n <DropdownMenuContent\n className=\"min-w-56 rounded-lg\"\n side={isMobile ? \"bottom\" : \"right\"}\n align=\"end\"\n sideOffset={4}\n >\n <div className=\"px-1 py-1.5 font-normal\">\n <div className=\"flex items-center gap-2 text-left text-sm\">\n <UserAvatar\n src={avatarSrc}\n alt={displayName}\n initials={initials}\n />\n <div className=\"grid flex-1 text-left text-sm leading-tight\">\n <span className=\"truncate font-medium\">{displayName}</span>\n {displayEmail ? (\n <span className=\"truncate text-xs text-muted-foreground\">\n {displayEmail}\n </span>\n ) : null}\n </div>\n </div>\n </div>\n <DropdownMenuSeparator />\n <DropdownMenuGroup>\n <DropdownMenuItem\n variant=\"destructive\"\n onClick={() => {\n void signOut();\n }}\n >\n <LogOut />\n Sign out\n </DropdownMenuItem>\n </DropdownMenuGroup>\n </DropdownMenuContent>\n </DropdownMenu>\n </SidebarMenuItem>\n </SidebarMenu>\n );\n}\n\nfunction UserAvatar({\n src,\n alt,\n initials,\n}: {\n src?: string;\n alt: string;\n initials: string;\n}) {\n return (\n <Avatar className=\"size-8\">\n {src ? <AvatarImage src={src} alt={alt} /> : null}\n <AvatarFallback className=\"text-xs\">{initials}</AvatarFallback>\n </Avatar>\n );\n}\n\nfunction getInitials(name?: string | null, email?: string | null): string {\n const trimmedName = name?.trim();\n if (trimmedName) {\n const parts = trimmedName.split(/\\s+/).filter(Boolean);\n if (parts.length >= 2) {\n return `${parts[0]?.[0] ?? \"\"}${parts[1]?.[0] ?? \"\"}`.toUpperCase();\n }\n return trimmedName.slice(0, 2).toUpperCase();\n }\n if (email) return email.slice(0, 2).toUpperCase();\n return \"??\";\n}\n\nasync function signOut(): Promise<void> {\n await fetch(\"/api/auth/sign-out\", {\n method: \"POST\",\n credentials: \"include\",\n });\n window.location.assign(\"/sign-in\");\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@silicajs/components",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Silica-aware, framework-agnostic React composables built on @silicajs/ui.",
5
5
  "type": "module",
6
6
  "types": "./dist/index.d.ts",
@@ -28,10 +28,10 @@
28
28
  "test": "vitest run --passWithNoTests"
29
29
  },
30
30
  "dependencies": {
31
- "@silicajs/core": "^0.1.0",
31
+ "@silicajs/core": "^0.1.2",
32
32
  "@silicajs/remark-obsidian": "^0.1.0",
33
- "@silicajs/ui": "^0.1.0",
34
- "lucide-react": "^1.16.0"
33
+ "@silicajs/ui": "^0.1.2",
34
+ "lucide-react": "^1.17.0"
35
35
  },
36
36
  "peerDependencies": {
37
37
  "react": "^19.2.0",
@@ -41,7 +41,16 @@
41
41
  "@types/react": "^19.2.10",
42
42
  "@types/react-dom": "^19.2.3",
43
43
  "react": "^19.2.6",
44
- "react-dom": "^19.2.6",
44
+ "react-dom": "^19.2.7",
45
45
  "tsup": "^8.5.1"
46
+ },
47
+ "homepage": "https://github.com/agdevhq/silica/tree/main/packages/components#readme",
48
+ "repository": {
49
+ "type": "git",
50
+ "url": "git+https://github.com/agdevhq/silica.git",
51
+ "directory": "packages/components"
52
+ },
53
+ "bugs": {
54
+ "url": "https://github.com/agdevhq/silica/issues"
46
55
  }
47
56
  }