@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.
- package/dist/google-icon.d.ts +7 -0
- package/dist/google-icon.js +46 -0
- package/dist/google-icon.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/not-found.js +7 -1
- package/dist/not-found.js.map +1 -1
- package/dist/search.js +14 -2
- package/dist/search.js.map +1 -1
- package/dist/sign-in-shell.d.ts +14 -0
- package/dist/sign-in-shell.js +50 -0
- package/dist/sign-in-shell.js.map +1 -0
- package/dist/user-menu.d.ts +2 -3
- package/dist/user-menu.js +118 -19
- package/dist/user-menu.js.map +1 -1
- package/package.json +14 -5
|
@@ -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(
|
|
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 {
|
package/dist/not-found.js.map
CHANGED
|
@@ -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
|
|
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.
|
|
143
|
-
result.
|
|
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
|
package/dist/search.js.map
CHANGED
|
@@ -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
|
|
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":[]}
|
package/dist/user-menu.d.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
|
|
3
3
|
type UserMenuProps = {
|
|
4
|
-
|
|
5
|
-
className?: string;
|
|
4
|
+
logo?: string;
|
|
6
5
|
};
|
|
7
|
-
declare function UserMenu({
|
|
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 {
|
|
4
|
-
import {
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
-
|
|
51
|
+
SidebarMenuButton,
|
|
20
52
|
{
|
|
21
|
-
|
|
22
|
-
|
|
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(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
};
|
package/dist/user-menu.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/user-menu.tsx"],"sourcesContent":["\"use client\";\n\nimport {
|
|
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":";AAyEc,cAOF,YAPE;AAvEd,SAAS,WAAW,gBAAgB;AACpC,SAAS,kBAAkB,cAAc;AAEzC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAgBA,SAAS,SAAS,EAAE,KAAK,GAAkB;AAChD,QAAM,EAAE,SAAS,IAAI,WAAW;AAChC,QAAM,CAAC,MAAM,OAAO,IAAI,SAA6B,IAAI;AAEzD,YAAU,MAAM;AACd,QAAI,YAAY;AAChB,SAAK,MAAM,yBAAyB,EAAE,aAAa,UAAU,CAAC,EAC3D,KAAK,CAAC,aAAc,SAAS,KAAK,SAAS,KAAK,IAAI,IAAK,EACzD,KAAK,CAAC,SAAiC;AACtC,UAAI,aAAa,CAAC,MAAM,KAAM;AAC9B,cAAQ;AAAA,QACN,MAAM,KAAK,KAAK,QAAQ,KAAK,KAAK,SAAS;AAAA,QAC3C,OAAO,KAAK,KAAK,SAAS;AAAA,QAC1B,OAAO,KAAK,KAAK,SAAS;AAAA,MAC5B,CAAC;AAAA,IACH,CAAC,EACA,MAAM,MAAM;AAAA,IAAC,CAAC;AAEjB,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,cAAc,MAAM,QAAQ;AAClC,QAAM,eAAe,MAAM,SAAS;AACpC,QAAM,YAAY,MAAM,SAAS;AACjC,QAAM,WAAW,YAAY,MAAM,MAAM,MAAM,KAAK;AAEpD,SACE,oBAAC,eACC,8BAAC,mBACC,+BAAC,gBACC;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,QACE;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,WAAU;AAAA;AAAA,QACZ;AAAA,QAGF;AAAA,8BAAC,cAAW,KAAK,WAAW,KAAK,aAAa,UAAoB;AAAA,UAClE,qBAAC,SAAI,WAAU,+CACb;AAAA,gCAAC,UAAK,WAAU,wBAAwB,uBAAY;AAAA,YACnD,eACC,oBAAC,UAAK,WAAU,0CACb,wBACH,IACE;AAAA,aACN;AAAA,UACA,oBAAC,oBAAiB,WAAU,iDAAgD;AAAA;AAAA;AAAA,IAC9E;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,MAAM,WAAW,WAAW;AAAA,QAC5B,OAAM;AAAA,QACN,YAAY;AAAA,QAEZ;AAAA,8BAAC,SAAI,WAAU,2BACb,+BAAC,SAAI,WAAU,6CACb;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,KAAK;AAAA,gBACL,KAAK;AAAA,gBACL;AAAA;AAAA,YACF;AAAA,YACA,qBAAC,SAAI,WAAU,+CACb;AAAA,kCAAC,UAAK,WAAU,wBAAwB,uBAAY;AAAA,cACnD,eACC,oBAAC,UAAK,WAAU,0CACb,wBACH,IACE;AAAA,eACN;AAAA,aACF,GACF;AAAA,UACA,oBAAC,yBAAsB;AAAA,UACvB,oBAAC,qBACC;AAAA,YAAC;AAAA;AAAA,cACC,SAAQ;AAAA,cACR,SAAS,MAAM;AACb,qBAAK,QAAQ;AAAA,cACf;AAAA,cAEA;AAAA,oCAAC,UAAO;AAAA,gBAAE;AAAA;AAAA;AAAA,UAEZ,GACF;AAAA;AAAA;AAAA,IACF;AAAA,KACF,GACF,GACF;AAEJ;AAEA,SAAS,WAAW;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,SACE,qBAAC,UAAO,WAAU,UACf;AAAA,UAAM,oBAAC,eAAY,KAAU,KAAU,IAAK;AAAA,IAC7C,oBAAC,kBAAe,WAAU,WAAW,oBAAS;AAAA,KAChD;AAEJ;AAEA,SAAS,YAAY,MAAsB,OAA+B;AACxE,QAAM,cAAc,MAAM,KAAK;AAC/B,MAAI,aAAa;AACf,UAAM,QAAQ,YAAY,MAAM,KAAK,EAAE,OAAO,OAAO;AACrD,QAAI,MAAM,UAAU,GAAG;AACrB,aAAO,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,YAAY;AAAA,IACpE;AACA,WAAO,YAAY,MAAM,GAAG,CAAC,EAAE,YAAY;AAAA,EAC7C;AACA,MAAI,MAAO,QAAO,MAAM,MAAM,GAAG,CAAC,EAAE,YAAY;AAChD,SAAO;AACT;AAEA,eAAe,UAAyB;AACtC,QAAM,MAAM,sBAAsB;AAAA,IAChC,QAAQ;AAAA,IACR,aAAa;AAAA,EACf,CAAC;AACD,SAAO,SAAS,OAAO,UAAU;AACnC;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@silicajs/components",
|
|
3
|
-
"version": "0.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.
|
|
31
|
+
"@silicajs/core": "^0.1.2",
|
|
32
32
|
"@silicajs/remark-obsidian": "^0.1.0",
|
|
33
|
-
"@silicajs/ui": "^0.1.
|
|
34
|
-
"lucide-react": "^1.
|
|
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.
|
|
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
|
}
|